From 1568e4854b522ab3c3ff1db526752b1bbcfd4828 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 01:07:45 +0100 Subject: [PATCH 01/29] feat(alerts): acknowledge alert incidents (core, API, MCP) An open incident can now be acknowledged ("someone is on it") and unacknowledged. The ack is a sub-state of `open`, like the telemetry hold: four nullable columns on alert_incidents (who, when, name, source), so every open/resolved consumer keeps working. alert_incident_events keeps the history (acknowledged, unacknowledged, ack_cleared) for the timeline and for on-call escalation to read. The lifecycle planner holds reminders and de-escalation notices while an incident is acknowledged. An escalation to a higher severity still pages and clears the ack. Resolve still sends the all-clear and clears it. Surfaces: POST /v2/alerts/incidents/:id/acknowledge and /unacknowledge (alerts:write, 409 on a resolved incident), GET /:id/events, an acknowledged=true|false list filter, and the acknowledge_alert_incident MCP tool (approval-gated in chat like the other mutating tools). --- apps/ai/src/mcp/dispatcher.ts | 2 + apps/ai/src/mcp/lib/alert-rules.ts | 9 + apps/ai/src/mcp/lib/params.ts | 13 +- apps/ai/src/mcp/tool-descriptors.generated.ts | 5 +- .../mcp/tools/__tests__/alert-tools.test.ts | 4 + .../tools/acknowledge-alert-incident.test.ts | 129 + .../mcp/tools/acknowledge-alert-incident.ts | 92 + .../ai/src/mcp/tools/get-incident-timeline.ts | 16 +- apps/ai/src/mcp/tools/list-alert-incidents.ts | 8 +- apps/ai/src/mcp/tools/mutating.ts | 1 + apps/ai/src/mcp/tools/registry.ts | 2 + apps/ai/src/mcp/tools/runtime-requirements.ts | 2 + .../test/mcp/unimplemented-tool-services.ts | 2 + .../api/src/routes/v2/alert-incidents.http.ts | 82 +- apps/api/src/routes/v2/alerts.http.test.ts | 108 + apps/api/src/routes/v2/v2-test-support.ts | 7 +- apps/api/src/runtime/service-graph.ts | 2 + apps/web/src/lab/alerts-fixture.ts | 4 + packages/alerting-core/src/index.ts | 19 +- packages/alerting-core/src/tiers.test.ts | 67 + .../alerts/AlertIncidentAckService.test.ts | 112 + .../alerts/AlertIncidentAckService.ts | 227 + .../services/alerts/AlertReadModelsService.ts | 23 +- .../src/services/alerts/AlertsService.test.ts | 88 + .../src/services/alerts/AlertsService.ts | 38 + .../src/services/org/OrganizationService.ts | 3 + .../migration.sql | 30 + .../snapshot.json | 18980 ++++++++++++++++ packages/db/src/tables/alerts.ts | 42 + packages/db/src/tables/index.ts | 2 + packages/domain/src/http/alerts.ts | 59 + .../domain/src/http/v2/alert-incidents.ts | 129 +- packages/domain/src/http/v2/openapi.test.ts | 3 + packages/domain/src/http/v2/resource-ids.ts | 2 + .../domain/src/http/v2/v2-contract.test.ts | 3 + packages/domain/src/mcp-outputs/alerts.ts | 16 + packages/domain/src/mcp-outputs/catalog.ts | 2 + packages/primitives/src/index.ts | 3 + packages/web-data/src/alerts/headroom.test.ts | 4 + .../src/alerts/incident-timeline.test.ts | 4 + .../src/alerts/overview-derive.test.ts | 4 + packages/web-data/src/alerts/overview.test.ts | 4 + .../src/alerts/preview-insights.test.ts | 4 + packages/web-data/src/collections/alerts.ts | 16 + 44 files changed, 20352 insertions(+), 20 deletions(-) create mode 100644 apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts create mode 100644 apps/ai/src/mcp/tools/acknowledge-alert-incident.ts create mode 100644 packages/backend/src/services/alerts/AlertIncidentAckService.test.ts create mode 100644 packages/backend/src/services/alerts/AlertIncidentAckService.ts create mode 100644 packages/db/drizzle/20261105234724_alert_incident_ack/migration.sql create mode 100644 packages/db/drizzle/20261105234724_alert_incident_ack/snapshot.json diff --git a/apps/ai/src/mcp/dispatcher.ts b/apps/ai/src/mcp/dispatcher.ts index bdcf068717..9c07575f90 100644 --- a/apps/ai/src/mcp/dispatcher.ts +++ b/apps/ai/src/mcp/dispatcher.ts @@ -10,6 +10,7 @@ import { RepoSandboxService, type RepositoryTarget } from "@maple/backend/servic import { AlertsService } from "@maple/backend/services/alerts/AlertsService" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" +import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" import { DashboardPersistenceService } from "@maple/backend/services/dashboards/DashboardPersistenceService" @@ -269,6 +270,7 @@ const MAX_PREPARED_REPOSITORIES = 3 const McpRuntimeServicesLive = Layer.mergeAll( AgentFeedbackService.layer, AlertReadModelsService.layer, + AlertIncidentAckService.layer, AlertRulesService.layer, AlertsService.layer, AuditLogService.layer, diff --git a/apps/ai/src/mcp/lib/alert-rules.ts b/apps/ai/src/mcp/lib/alert-rules.ts index e53ed25769..4943da9d11 100644 --- a/apps/ai/src/mcp/lib/alert-rules.ts +++ b/apps/ai/src/mcp/lib/alert-rules.ts @@ -9,6 +9,7 @@ import { AlertRuleId, AlertSeverity, AlertSignalType, + type AlertIncidentDocument, type AlertRuleDocument, type AlertRuleDestinationNotFoundError, type AlertRuleNotFoundError, @@ -286,3 +287,11 @@ export const renderRuleWrite = ( ], } } + +/** Who acknowledged an open incident, for the model; null when nobody has. */ +export const ackLabel = ( + incident: Pick, +): string | null => + incident.acknowledgedAt === null + ? null + : (incident.acknowledgedByName ?? `someone via ${incident.acknowledgedVia ?? "Maple"}`) diff --git a/apps/ai/src/mcp/lib/params.ts b/apps/ai/src/mcp/lib/params.ts index 78b777e545..2ee14fe336 100644 --- a/apps/ai/src/mcp/lib/params.ts +++ b/apps/ai/src/mcp/lib/params.ts @@ -18,7 +18,13 @@ import { String as Str, } from "effect" import { decodePublicId, PublicIdPrefixes } from "@maple/domain/http/v2" -import { AlertDestinationId, AlertRuleId, DashboardId, ErrorIssueId } from "@maple/domain/primitives" +import { + AlertDestinationId, + AlertIncidentId, + AlertRuleId, + DashboardId, + ErrorIssueId, +} from "@maple/domain/primitives" import { WarehouseTimeInput, type WarehouseDateTime } from "@maple/query-engine" import { McpInvalidInputError } from "../tools/types" import { resolveWindow } from "./time" @@ -141,6 +147,11 @@ export const optionalList = (description: string) => /** The resources a tool takes ids for, with their REST public-id prefix and internal id schema. */ const RESOURCE_IDS = { alertRule: { prefix: PublicIdPrefixes.alertRule, schema: AlertRuleId, noun: "alert rule" }, + alertIncident: { + prefix: PublicIdPrefixes.alertIncident, + schema: AlertIncidentId, + noun: "alert incident", + }, alertDestination: { prefix: PublicIdPrefixes.alertDestination, schema: AlertDestinationId, diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index cc79e1ed5e..11e322ff5d 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -30,13 +30,14 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"list_alert_rules","title":"List Alert Rules","description":"List configured alert rules with severity, signal type and condition. Use list_alert_incidents for what has fired.","inputSchema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"string"},"description":"Only rules covering any of these services: scoped to them, scoped to every service, or raw_query rules whose SQL or name mentions them"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"Only rules on this signal type"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only rules with this severity"},"enabled_only":{"type":"boolean","description":"Only enabled rules"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"services":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"severity":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["rules","total"],"additionalProperties":true},"annotations":{"title":"List Alert Rules","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_destinations","title":"List Alert Destinations","description":"List the notification destinations a rule can deliver to, with id, type and delivery health. Pass the ids as destination_ids to create_alert_rule / update_alert_rule.","inputSchema":{"type":"object","properties":{"type":{"type":"string","enum":["pagerduty","webhook","discord","telegram","google-chat","email","chat","linear"],"description":"Only destinations of this type"},"enabled_only":{"type":"boolean","description":"Only enabled destinations"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"enabled":{"type":"boolean"},"summary":{"type":"string"},"channelLabel":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestError":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabledReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","type","enabled","summary","channelLabel","lastTestedAt","lastTestError","disabledReason","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"type":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["destinations","total"],"additionalProperties":true},"annotations":{"title":"List Alert Destinations","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_alert_rule","title":"Get Alert Rule","description":"Get full configuration details of a specific alert rule including thresholds, service filters, evaluation settings, and notification destinations. Use list_alert_rules to find rule IDs.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"excludeServiceNames":{"type":"array","items":{"type":"string"}},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noDataBehavior":{"type":"string"},"consecutiveBreachesRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthyRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"renotifyIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"queryBuilderDraft":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"null"}]},"rawQuerySql":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawQueryReducer":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"notificationTitle":{"anyOf":[{"type":"string"},{"type":"null"}]},"notificationBody":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationError":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationErrorKind":{"anyOf":[{"type":"string"},{"type":"null"}]},"slowedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}},"required":["id","name","type","enabled"],"additionalProperties":true}}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt","excludeServiceNames","groupBy","minimumSampleCount","noDataBehavior","consecutiveBreachesRequired","consecutiveHealthyRequired","renotifyIntervalMinutes","apdexThresholdMs","queryBuilderDraft","rawQuerySql","rawQueryReducer"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Get Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"list_alert_incidents","title":"List Alert Incidents","description":"Alert incidents: alert-rule breaches, open and resolved, with severity, group, condition and last observed value (flare-ups of an error issue are list_error_incidents). For one rule's trigger, notify and resolve timestamps use get_incident_timeline.","inputSchema":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 50, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"holdReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"heldSince":{"anyOf":[{"type":"string"},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"firstTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","holdReason","heldSince","threshold","comparator","firstTriggeredAt","resolvedAt","lastObservedValue"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"recentlyResolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"List Alert Incidents","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"list_alert_incidents","title":"List Alert Incidents","description":"Alert incidents: alert-rule breaches, open and resolved, with severity, group, condition and last observed value (flare-ups of an error issue are list_error_incidents). For one rule's trigger, notify and resolve timestamps use get_incident_timeline.","inputSchema":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 50, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"holdReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"heldSince":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"firstTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","holdReason","heldSince","threshold","comparator","firstTriggeredAt","resolvedAt","lastObservedValue"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"recentlyResolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"List Alert Incidents","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_checks","title":"List Alert Checks","description":"List a rule's recent checks (one row per evaluation) with observed value, threshold, sample count and incident transition. Use it to tune thresholds, diagnose flapping, or see the near-misses before a breach.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"group_key":{"type":"string","description":"Only checks for this exact group key"},"status":{"type":"string","enum":["breached","healthy","skipped","error"],"description":"Only checks with this status"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"limit":{"type":"number","description":"Max checks to return (default 100, max 2000)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"skipped":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errored":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transitions":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"checks":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"string"},"groupKey":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"observedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowStart":{"type":"string"},"windowEnd":{"type":"string"},"consecutiveBreaches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"incidentId":{"anyOf":[{"type":"string"},{"type":"null"}]},"incidentTransition":{"type":"string"},"evaluationDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errorMessage":{"anyOf":[{"type":"string"},{"type":"null"}]},"errorCategory":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["timestamp","groupKey","status","skipReason","observedValue","threshold","comparator","sampleCount","windowStart","windowEnd","consecutiveBreaches","consecutiveHealthy","incidentId","incidentTransition","evaluationDurationMs","errorMessage","errorCategory"],"additionalProperties":true}},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"groupKey":{"type":"string"},"status":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["ruleId","total","breached","healthy","skipped","noData","belowMinSamples","errored","transitions","checks"],"additionalProperties":true},"annotations":{"title":"List Alert Checks","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations).","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_alert_rule","title":"Delete Alert Rule","description":"Permanently delete an alert rule together with its incident history, delivery events and evaluation state. Irreversible; requires confirm=true. Ids from list_alert_rules.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"confirm":{"type":"boolean","description":"Must be true. There is no undo."}},"required":["rule_id","confirm"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"annotations":{"title":"Delete Alert Rule","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, + {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_dashboard","title":"Get Dashboard","description":"Full configuration of one dashboard: every widget with its id, visualization, dataSource, display, layout and any pinned timeRange. Widgets come back in the shape update_dashboard_widget and replace_dashboard_widgets take. Ids from list_dashboards.","inputSchema":{"type":"object","properties":{"dashboard_id":{"type":"string","description":"Dashboard ID: its UUID or its REST id (dash_\u2026)"}},"required":["dashboard_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"publicId":{"type":"string"},"dashboard":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"widgets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"visualization":{"type":"string","enum":["chart","hbar","stat","gauge","table","list","pie","histogram","heatmap","funnel","paths","markdown"]},"dataSource":{"$ref":"#/$defs/@maple~1WidgetDataSource"},"display":{"type":"object","properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartPresentation":{"anyOf":[{"type":"object","properties":{"legend":{"anyOf":[{"type":"string","enum":["visible","hidden","right"]},{"type":"null"}]},"seriesStats":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tooltip":{"anyOf":[{"type":"string","enum":["visible","hidden"]},{"type":"null"}]},"showPoints":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fillNulls":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"boolean","enum":[false]},{"type":"null"}]},"compareToPreviousPeriod":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"xAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"yAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMin":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMax":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScale":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fitYAxisToData":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"seriesMapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"colorOverrides":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"stacked":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"curveType":{"anyOf":[{"type":"string","enum":["linear","monotone"]},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}]},"sparkline":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"dataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1WidgetDataSource"},{"type":"null"}]}},"required":["enabled"],"additionalProperties":true},{"type":"null"}]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"header":{"type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"align":{"anyOf":[{"type":"string","enum":["left","center","right"]},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]}},"required":["field","header"],"additionalProperties":true}},{"type":"null"}]},"listDataSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"listWhereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"listLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"listRootOnly":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"pie":{"anyOf":[{"type":"object","properties":{"donut":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"innerRadius":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"showLabels":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"showPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"funnel":{"anyOf":[{"type":"object","properties":{"showStepPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["session"]},"dimension":{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},"value":{"type":"string"}},"required":["kind","dimension","value"],"additionalProperties":true}]}},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"breakdownBy":{"anyOf":[{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},{"type":"string","pattern":"^attribute:[\\s\\S]*?$"},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","enum":["bars","dropoff"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"paths":{"anyOf":[{"type":"object","properties":{"anchor":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true}]},"direction":{"anyOf":[{"type":"string","enum":["after","before"]},{"type":"null"}]},"depth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"branches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"include":{"anyOf":[{"type":"string","enum":["all","events","pages"]},{"type":"null"}]},"exclude":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]}},"required":["anchor"],"additionalProperties":true},{"type":"null"}]},"histogram":{"anyOf":[{"type":"object","properties":{"bucketCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"bucketWidth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScaleY":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"heatmap":{"anyOf":[{"type":"object","properties":{"colorScale":{"anyOf":[{"type":"string","enum":["amber","blues","reds","viridis","magma","cividis"]},{"type":"null"}]},"scaleType":{"anyOf":[{"type":"string","enum":["linear","log"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"gauge":{"anyOf":[{"type":"object","properties":{"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"style":{"anyOf":[{"type":"string","enum":["radial","bar"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"markdown":{"anyOf":[{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"layout":{"type":"object","properties":{"x":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"y":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"w":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"h":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["x","y","w","h"],"additionalProperties":true},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"sectionId":{"type":"string"},"tabId":{"type":"string"}},"required":["id","visualization","dataSource","display","layout"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","tags","timeRange","widgets","createdAt","updatedAt"],"additionalProperties":true}},"required":["publicId","dashboard"],"additionalProperties":true,"$defs":{"@maple/IsoDateTimeString":{"type":"string"},"@maple/QueryWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["query"]},"resultShape":{"type":"string","enum":["timeseries","breakdown","list"]},"queries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true}]}},"formulas":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expression":{"type":"string"},"legend":{"type":"string"},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","expression","legend"],"additionalProperties":true}},{"type":"null"}]},"comparison":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"type":"string","enum":["none","previous_period"]},{"type":"null"}]},"includePercentChange":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"defaultLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","resultShape","queries"],"additionalProperties":true,"title":"Query widget data source"},"@maple/RawSqlWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["raw_sql"]},"sql":{"type":"string"},"displayType":{"type":"string"},"granularitySeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","sql"],"additionalProperties":true,"title":"Raw SQL widget data source"},"@maple/RouteWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["route"]},"endpoint":{"type":"string"},"params":{"type":"object"},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","endpoint"],"additionalProperties":true,"title":"Route widget data source"},"@maple/StaticWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["static"]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind"],"additionalProperties":true,"title":"Static widget data source"},"@maple/WidgetDataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1QueryWidgetDataSource"},{"$ref":"#/$defs/@maple~1RawSqlWidgetDataSource"},{"$ref":"#/$defs/@maple~1RouteWidgetDataSource"},{"$ref":"#/$defs/@maple~1StaticWidgetDataSource"}],"title":"Widget data source"}}},"annotations":{"title":"Get Dashboard","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts b/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts index a0b526b6ae..2d446f0d4e 100644 --- a/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts +++ b/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts @@ -135,6 +135,10 @@ const incident = (overrides: Record) => ({ lastNotifiedAt: null, holdReason: null, heldSince: null, + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, errorIssueId: null, ...overrides, }) diff --git a/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts b/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts new file mode 100644 index 0000000000..7e0e0a3295 --- /dev/null +++ b/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts @@ -0,0 +1,129 @@ +import { afterEach, assert, describe, it } from "@effect/vitest" +import { ConfigProvider, Effect, Layer, Schema } from "effect" +import { OrgId, UserId } from "@maple/domain/http" +import { Env } from "@maple/backend/platform/Env" +import { EdgeCacheServiceLive } from "@maple/backend/platform/CacheBackendLive" +import { cleanupTestDbs, createTestDb, executeSql, type TestDb } from "@maple/backend/platform/test-pglite" +import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" +import { CurrentMcpTenant } from "../lib/query-warehouse" +import { registerAcknowledgeAlertIncidentTool } from "./acknowledge-alert-incident" +import type { McpToolError, McpToolRegistrar } from "./types" +import type { McpToolRequirements } from "./runtime-requirements" +import { unimplementedMcpToolServices } from "../../../test/mcp/unimplemented-tool-services" + +const trackedDbs: TestDb[] = [] +afterEach(() => cleanupTestDbs(trackedDbs)) + +const ORG = "org_mcp_ack" +const OPEN = "00000000-0000-4000-8000-0000000000b1" +const RESOLVED = "00000000-0000-4000-8000-0000000000b2" + +type Invoke = ( + params: Readonly>, +) => Effect.Effect + +const acknowledgeTool = (): Invoke => { + let invoke: Invoke = () => Effect.die("unregistered") + const registrar: McpToolRegistrar = { + define: (spec) => { + invoke = (params) => + Schema.decodeEffect(spec.parameters)(params).pipe(Effect.orDie, Effect.flatMap(spec.handler)) + }, + } + registerAcknowledgeAlertIncidentTool(registrar) + return (params) => invoke(params) +} + +const layerFor = (testDb: TestDb) => + Layer.mergeAll( + unimplementedMcpToolServices, + AlertIncidentAckService.layer, + Layer.succeed(CurrentMcpTenant, { + orgId: Schema.decodeSync(OrgId)(ORG), + userId: Schema.decodeSync(UserId)("user_mcp_ack"), + roles: [], + authMode: "self_hosted", + }), + ).pipe( + Layer.provide(Layer.mergeAll(testDb.layer, EdgeCacheServiceLive)), + Layer.provideMerge(Env.layer), + Layer.provide( + ConfigProvider.layer( + ConfigProvider.fromUnknown({ + TINYBIRD_HOST: "https://api.tinybird.co", + TINYBIRD_TOKEN: "test-token", + MAPLE_AUTH_MODE: "self_hosted", + MAPLE_ROOT_PASSWORD: "test-root-password", + MAPLE_DEFAULT_ORG_ID: "default", + MAPLE_INGEST_KEY_ENCRYPTION_KEY: Buffer.alloc(32, 1).toString("base64"), + MAPLE_INGEST_KEY_LOOKUP_HMAC_KEY: "maple-test-lookup-secret", + INTERNAL_SERVICE_TOKEN: "test-internal-token", + }), + ), + ), + ) + +const seed = (testDb: TestDb) => + Effect.promise(() => + executeSql( + testDb, + `INSERT INTO alert_incidents + (id, org_id, rule_id, incident_key, rule_name, group_key, signal_type, severity, status, + comparator, threshold, first_triggered_at, last_triggered_at, resolved_at, + dedupe_key, created_at, updated_at) + VALUES + ($1, $3, '00000000-0000-4000-8000-0000000000b0', 'mcp-open', 'Checkout errors', '__total__', + 'error_rate', 'critical', 'open', 'gt', 5, now(), now(), null, 'm-open', now(), now()), + ($2, $3, '00000000-0000-4000-8000-0000000000b0', 'mcp-res', 'Checkout errors', '__total__', + 'error_rate', 'critical', 'resolved', 'gt', 5, now(), now(), now(), 'm-res', now(), now())`, + [OPEN, RESOLVED, ORG], + ), + ) + +describe("acknowledge_alert_incident", () => { + it.effect("acknowledges by REST id, unacknowledges, and reports a no-op", () => { + const testDb = createTestDb(trackedDbs) + const call = acknowledgeTool() + return Effect.gen(function* () { + yield* seed(testDb) + const acked = yield* call({ incident_id: OPEN }) + assert.deepInclude(acked, { + id: OPEN, + status: "open", + acknowledged: true, + changed: true, + // MCP has no member directory: the source stands in for the name. + acknowledgedBy: "someone via mcp", + }) + assert.deepInclude(yield* call({ incident_id: OPEN, acknowledged: "true" }), { changed: false }) + assert.deepInclude(yield* call({ incident_id: OPEN, acknowledged: false }), { + acknowledged: false, + acknowledgedBy: null, + changed: true, + }) + }).pipe(Effect.provide(layerFor(testDb))) + }) + + it.effect("names a resolved or unknown incident as invalid input on incident_id", () => { + const testDb = createTestDb(trackedDbs) + const call = acknowledgeTool() + return Effect.gen(function* () { + yield* seed(testDb) + const resolved = yield* Effect.flip(call({ incident_id: RESOLVED })) + assert.deepInclude(resolved, { + _tag: "@maple/mcp/errors/McpInvalidInputError", + parameter: "incident_id", + }) + const unknown = yield* Effect.flip(call({ incident_id: "00000000-0000-4000-8000-0000000000ff" })) + assert.deepInclude(unknown, { + _tag: "@maple/mcp/errors/McpInvalidInputError", + parameter: "incident_id", + }) + const malformed = yield* Effect.flip(call({ incident_id: "dest_notAnIncident" })) + assert.deepInclude(malformed, { + _tag: "@maple/mcp/errors/McpInvalidInputError", + parameter: "incident_id", + }) + }).pipe(Effect.provide(layerFor(testDb))) + }) +}) diff --git a/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts new file mode 100644 index 0000000000..3491456709 --- /dev/null +++ b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts @@ -0,0 +1,92 @@ +import { McpInvalidInputError, McpQueryError, type McpToolRegistrar } from "./types" +import { Effect, Schema } from "effect" +import { AcknowledgeAlertIncidentOutput } from "@maple/domain/mcp-outputs" +import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" +import { CurrentMcpTenant } from "../lib/query-warehouse" +import { ackLabel } from "../lib/alert-rules" +import * as P from "../lib/params" +import { doc } from "../lib/tool-doc" + +export function registerAcknowledgeAlertIncidentTool(server: McpToolRegistrar) { + server.define({ + name: "acknowledge_alert_incident", + title: "Acknowledge Alert Incident", + description: + "Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. " + + "While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. " + + "Only open incidents; ids from list_alert_incidents.", + parameters: Schema.Struct({ + incident_id: P.resourceId("alertIncident", "Alert incident ID"), + acknowledged: P.optionalFlag("false to unacknowledge. Default true."), + }), + output: AcknowledgeAlertIncidentOutput, + hints: { readOnly: false, destructive: false, idempotent: true }, + phrases: ["Acknowledging an alert incident"], + handler: Effect.fn("McpTool.acknowledgeAlertIncident")(function* (params) { + const incidentId = yield* P.decodeResourceId("alertIncident", "incident_id")(params.incident_id) + const tenant = yield* CurrentMcpTenant + const acks = yield* AlertIncidentAckService + const acknowledge = params.acknowledged !== false + const actor = { userId: tenant.userId, name: null, source: "mcp" as const } + + const result = yield* ( + acknowledge + ? acks.acknowledge(tenant.orgId, incidentId, actor) + : acks.unacknowledge(tenant.orgId, incidentId, actor) + ).pipe( + Effect.catchTags({ + "@maple/http/errors/AlertIncidentNotFoundError": () => + Effect.fail( + new McpInvalidInputError({ + message: `No alert incident ${params.incident_id}. List incidents with list_alert_incidents.`, + parameter: "incident_id", + }), + ), + "@maple/http/errors/AlertIncidentResolvedError": (error) => + Effect.fail( + new McpInvalidInputError({ message: error.message, parameter: "incident_id" }), + ), + "@maple/http/errors/AlertPersistenceError": (error) => + Effect.fail( + new McpQueryError({ + message: `${error._tag}: ${error.message}`, + pipeName: "acknowledge_alert_incident", + cause: error, + }), + ), + }), + ) + + const { incident } = result + return { + id: incident.id, + ruleName: incident.ruleName, + status: incident.status, + acknowledged: incident.acknowledgedAt !== null, + acknowledgedAt: incident.acknowledgedAt, + acknowledgedBy: ackLabel(incident), + changed: result.changed, + } + }), + render: (output) => ({ + title: output.acknowledged ? "Incident Acknowledged" : "Incident Unacknowledged", + blocks: [ + ...(output.changed + ? [] + : [ + doc.text( + `Already ${output.acknowledged ? "acknowledged" : "unacknowledged"}; nothing changed.`, + ), + ]), + doc.fields([ + ["Incident", output.id], + ["Rule", output.ruleName], + ["Status", output.status], + ["Acknowledged by", output.acknowledgedBy ?? undefined], + ["Acknowledged at", output.acknowledgedAt ?? undefined], + ]), + ], + next: [doc.next("get_incident_timeline", {}, "the incident timeline")], + }), + }) +} diff --git a/apps/ai/src/mcp/tools/get-incident-timeline.ts b/apps/ai/src/mcp/tools/get-incident-timeline.ts index 3b89f78735..8534cc0232 100644 --- a/apps/ai/src/mcp/tools/get-incident-timeline.ts +++ b/apps/ai/src/mcp/tools/get-incident-timeline.ts @@ -3,7 +3,13 @@ import { Effect, Schema, Array as Arr, String as Str } from "effect" import { GetIncidentTimelineOutput } from "@maple/domain/mcp-outputs" import { CurrentMcpTenant } from "../lib/query-warehouse" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" -import { ALERT_INCIDENT_STATUSES, ALERT_SEVERITIES, formatCondition, parseRuleId } from "../lib/alert-rules" +import { + ackLabel, + ALERT_INCIDENT_STATUSES, + ALERT_SEVERITIES, + formatCondition, + parseRuleId, +} from "../lib/alert-rules" import * as P from "../lib/params" import { doc, type DocBlock } from "../lib/tool-doc" @@ -82,6 +88,8 @@ export function registerGetIncidentTimelineTool(server: McpToolRegistrar) { lastTriggeredAt: i.lastTriggeredAt, resolvedAt: i.resolvedAt, lastNotifiedAt: i.lastNotifiedAt, + acknowledgedAt: i.acknowledgedAt, + acknowledgedBy: ackLabel(i), })), total: incidents.length, openCount: Arr.filter(incidents, (i) => i.status === "open").length, @@ -120,6 +128,12 @@ export function registerGetIncidentTimelineTool(server: McpToolRegistrar) { ["Last triggered", formatTimestamp(inc.lastTriggeredAt)], ["Resolved at", formatTimestamp(inc.resolvedAt)], ["Last notified", formatTimestamp(inc.lastNotifiedAt)], + [ + "Acknowledged", + inc.acknowledgedAt == null + ? undefined + : `${formatTimestamp(inc.acknowledgedAt)} by ${inc.acknowledgedBy ?? "someone"}`, + ], ["Incident ID", inc.id], ["Rule ID", inc.ruleId], ]), diff --git a/apps/ai/src/mcp/tools/list-alert-incidents.ts b/apps/ai/src/mcp/tools/list-alert-incidents.ts index 55e689c341..50cba7c9b0 100644 --- a/apps/ai/src/mcp/tools/list-alert-incidents.ts +++ b/apps/ai/src/mcp/tools/list-alert-incidents.ts @@ -4,7 +4,7 @@ import { Clock, Effect, Schema, Array as Arr, String as Str } from "effect" import { ListAlertIncidentsOutput } from "@maple/domain/mcp-outputs" import { CurrentMcpTenant } from "../lib/query-warehouse" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" -import { ALERT_INCIDENT_STATUSES, ALERT_SEVERITIES, formatCondition } from "../lib/alert-rules" +import { ackLabel, ALERT_INCIDENT_STATUSES, ALERT_SEVERITIES, formatCondition } from "../lib/alert-rules" import * as P from "../lib/params" import { doc } from "../lib/tool-doc" import { parseWarehouseDateTime } from "@maple/query-engine" @@ -105,6 +105,8 @@ export function registerListAlertIncidentsTool(server: McpToolRegistrar) { status: i.status, holdReason: i.holdReason, heldSince: i.heldSince, + acknowledgedAt: i.acknowledgedAt, + acknowledgedBy: ackLabel(i), threshold: i.threshold, comparator: i.comparator, firstTriggeredAt: i.firstTriggeredAt, @@ -199,7 +201,9 @@ export function registerListAlertIncidentsTool(server: McpToolRegistrar) { i.severity, i.holdReason != null ? `open (waiting on data: ${i.holdReason})` - : i.status, + : i.acknowledgedAt != null + ? `open (acknowledged by ${i.acknowledgedBy ?? "someone"})` + : i.status, i.groupKey ?? "all", i.signalType, formatCondition(i), diff --git a/apps/ai/src/mcp/tools/mutating.ts b/apps/ai/src/mcp/tools/mutating.ts index 8f50153e5d..f97be328e6 100644 --- a/apps/ai/src/mcp/tools/mutating.ts +++ b/apps/ai/src/mcp/tools/mutating.ts @@ -18,6 +18,7 @@ export const MUTATING_TOOL_NAMES: ReadonlySet = new Set([ "create_alert_rule", "update_alert_rule", "delete_alert_rule", + "acknowledge_alert_incident", // error issues "claim_error_issue", "release_error_issue", diff --git a/apps/ai/src/mcp/tools/registry.ts b/apps/ai/src/mcp/tools/registry.ts index 9cba7fac8c..2d974ac9f0 100644 --- a/apps/ai/src/mcp/tools/registry.ts +++ b/apps/ai/src/mcp/tools/registry.ts @@ -10,6 +10,7 @@ import { registerCreateAlertRuleTool } from "./create-alert-rule" import { registerPreviewAlertRuleTool } from "./preview-alert-rule" import { registerUpdateAlertRuleTool } from "./update-alert-rule" import { registerDeleteAlertRuleTool } from "./delete-alert-rule" +import { registerAcknowledgeAlertIncidentTool } from "./acknowledge-alert-incident" import { registerCreateDashboardTool } from "./create-dashboard" import { registerDescribeDashboardSchemaTool } from "./describe-dashboard-schema" import { registerDiagnoseServiceTool } from "./diagnose-service" @@ -429,6 +430,7 @@ const collectMapleToolDefinitions = (): ReadonlyArray => { registerCreateAlertRuleTool(registrar) registerUpdateAlertRuleTool(registrar) registerDeleteAlertRuleTool(registrar) + registerAcknowledgeAlertIncidentTool(registrar) registerDescribeDashboardSchemaTool(registrar) registerListDashboardsTool(registrar) registerGetDashboardTool(registrar) diff --git a/apps/ai/src/mcp/tools/runtime-requirements.ts b/apps/ai/src/mcp/tools/runtime-requirements.ts index 9cacd010ac..340927704f 100644 --- a/apps/ai/src/mcp/tools/runtime-requirements.ts +++ b/apps/ai/src/mcp/tools/runtime-requirements.ts @@ -2,6 +2,7 @@ import type { AuditLogService } from "@maple/backend/services/audit/AuditLogServ import type { AgentFeedbackService } from "@maple/backend/services/feedback/AgentFeedbackService" import type { AlertsService } from "@maple/backend/services/alerts/AlertsService" import type { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" +import type { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import type { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" import type { DashboardPersistenceService } from "@maple/backend/services/dashboards/DashboardPersistenceService" import type { ErrorActorsService } from "@maple/backend/services/errors/ErrorActorsService" @@ -45,6 +46,7 @@ export type McpToolRuntimeRequirements = | AuditLogService | DependencyScanReader | AlertReadModelsService + | AlertIncidentAckService | AlertRulesService | DashboardPersistenceService | ErrorActorsService diff --git a/apps/ai/test/mcp/unimplemented-tool-services.ts b/apps/ai/test/mcp/unimplemented-tool-services.ts index 8e2574fd24..ce8079677c 100644 --- a/apps/ai/test/mcp/unimplemented-tool-services.ts +++ b/apps/ai/test/mcp/unimplemented-tool-services.ts @@ -5,6 +5,7 @@ import { AuditLogService } from "@maple/backend/services/audit/AuditLogService" import { AgentFeedbackService } from "@maple/backend/services/feedback/AgentFeedbackService" import { AlertsService } from "@maple/backend/services/alerts/AlertsService" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" +import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" import { DashboardPersistenceService } from "@maple/backend/services/dashboards/DashboardPersistenceService" import { ErrorActorsService } from "@maple/backend/services/errors/ErrorActorsService" @@ -41,6 +42,7 @@ export const unimplementedMcpToolServices = Layer.mergeAll( Layer.mock(AgentFeedbackService)({}), Layer.mock(AlertsService)({}), Layer.mock(AlertReadModelsService)({}), + Layer.mock(AlertIncidentAckService)({}), Layer.mock(AlertRulesService)({}), Layer.mock(DashboardPersistenceService)({}), Layer.mock(ErrorActorsService)({}), diff --git a/apps/api/src/routes/v2/alert-incidents.http.ts b/apps/api/src/routes/v2/alert-incidents.http.ts index dd7bbf2538..6ba0a28a02 100644 --- a/apps/api/src/routes/v2/alert-incidents.http.ts +++ b/apps/api/src/routes/v2/alert-incidents.http.ts @@ -1,10 +1,16 @@ import { HttpApiBuilder } from "effect/http-api" -import type { AlertIncidentDocument } from "@maple/domain/http" +import type { AlertIncidentDocument, AlertIncidentEventDocument } from "@maple/domain/http" import { CurrentTenant } from "@maple/domain/http" -import type { V2AlertIncident } from "@maple/domain/http/v2" +import type { V2AlertIncident, V2AlertIncidentEvent } from "@maple/domain/http/v2" import { MapleApiV2, paginateOffsetQuery } from "@maple/domain/http/v2" -import { Effect, Array as Arr } from "effect" +import { Duration, Effect, Option, Array as Arr } from "effect" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" +import { + AlertIncidentAckService, + type AlertIncidentActor, +} from "@maple/backend/services/alerts/AlertIncidentAckService" +import { OrgMembersService } from "@maple/backend/services/org/OrgMembersService" +import { CurrentAuditActor } from "@maple/backend/services/auth/audit-actor" const toV2Incident = (doc: AlertIncidentDocument): V2AlertIncident => ({ id: doc.id, @@ -29,12 +35,58 @@ const toV2Incident = (doc: AlertIncidentDocument): V2AlertIncident => ({ last_notified_at: doc.lastNotifiedAt, hold_reason: doc.holdReason, held_since: doc.heldSince, + acknowledged_at: doc.acknowledgedAt, + acknowledged_by: + doc.acknowledgedAt === null + ? null + : { user_id: doc.acknowledgedByUserId, name: doc.acknowledgedByName }, + acknowledged_via: doc.acknowledgedVia, error_issue_id: doc.errorIssueId, }) +const toV2Event = (doc: AlertIncidentEventDocument): V2AlertIncidentEvent => ({ + id: doc.id, + object: "alert_incident_event", + incident_id: doc.incidentId, + type: doc.type, + actor: + doc.actorUserId === null && doc.actorName === null + ? null + : { user_id: doc.actorUserId, name: doc.actorName }, + source: doc.source, + created_at: doc.createdAt, +}) + export const HttpV2AlertIncidentsLive = HttpApiBuilder.group(MapleApiV2, "alertIncidents", (handlers) => Effect.gen(function* () { const readModels = yield* AlertReadModelsService + const acks = yield* AlertIncidentAckService + const members = yield* OrgMembersService + + // Who is acknowledging, with a name recorded alongside so every surface can show it without a + // directory lookup. An API key is named by its label; a session by the member directory, best + // effort: a slow or unconfigured directory records the ack without a name. + const callerActor = Effect.gen(function* () { + const tenant = yield* CurrentTenant.Context + const credential = yield* CurrentAuditActor + const name = + credential?.type === "api_key" + ? (credential.label ?? null) + : Option.getOrNull( + (yield* members + .resolveMembers(tenant.orgId, [tenant.userId]) + .pipe(Effect.timeout(Duration.seconds(3)), Effect.option)).pipe( + Option.flatMap(Arr.head), + Option.map((member) => member.name ?? member.email), + ), + ) + const actor: AlertIncidentActor = { + userId: tenant.userId, + name, + source: credential?.source === "dashboard" ? "web" : "api", + } + return { orgId: tenant.orgId, actor } + }) return handlers .handle("list", ({ query }) => @@ -45,6 +97,9 @@ export const HttpV2AlertIncidentsLive = HttpApiBuilder.group(MapleApiV2, "alertI .listIncidents(tenant.orgId, { ...(query.status !== undefined ? { status: query.status } : undefined), ...(query.rule_id !== undefined ? { ruleId: query.rule_id } : undefined), + ...(query.acknowledged !== undefined + ? { acknowledged: query.acknowledged === "true" } + : undefined), limit, offset, }) @@ -61,5 +116,26 @@ export const HttpV2AlertIncidentsLive = HttpApiBuilder.group(MapleApiV2, "alertI return toV2Incident(incident) }), ) + .handle("acknowledge", ({ params }) => + Effect.gen(function* () { + const { orgId, actor } = yield* callerActor + const result = yield* acks.acknowledge(orgId, params.id, actor) + return toV2Incident(result.incident) + }).pipe(Effect.withSpan("HttpV2AlertIncidents.acknowledge")), + ) + .handle("unacknowledge", ({ params }) => + Effect.gen(function* () { + const { orgId, actor } = yield* callerActor + const result = yield* acks.unacknowledge(orgId, params.id, actor) + return toV2Incident(result.incident) + }).pipe(Effect.withSpan("HttpV2AlertIncidents.unacknowledge")), + ) + .handle("listEvents", ({ params }) => + Effect.gen(function* () { + const tenant = yield* CurrentTenant.Context + const events = yield* acks.listEvents(tenant.orgId, params.id) + return { object: "list" as const, data: Arr.map(events, toV2Event) } + }), + ) }), ) diff --git a/apps/api/src/routes/v2/alerts.http.test.ts b/apps/api/src/routes/v2/alerts.http.test.ts index 0c49db26a0..facfbb506f 100644 --- a/apps/api/src/routes/v2/alerts.http.test.ts +++ b/apps/api/src/routes/v2/alerts.http.test.ts @@ -1146,6 +1146,114 @@ describe("v2 alerts over HTTP", () => { await harness.dispose() }) + it("acknowledges and unacknowledges an open incident and lists its events", async () => { + const harness = makeHarness() + const key = await harness.bootstrapKey(["alerts:write"]) + const readOnly = await harness.bootstrapKey(["alerts:read"]) + await createWebhookAndRule(harness, key.secret) + await executeSql( + harness.testDb, + `insert into alert_incidents ( + id, org_id, rule_id, incident_key, rule_name, group_key, signal_type, severity, status, + comparator, threshold, first_triggered_at, last_triggered_at, resolved_at, dedupe_key, + created_at, updated_at + ) + select v.id, rules.org_id, rules.id, v.id, 'Checkout error rate', '__total__', 'error_rate', + 'critical', v.status, 'gt', 0.05, now(), now(), v.resolved_at, v.id, now(), now() + from (values + ('00000000-0000-4000-8000-0000000000a1', 'open', null::timestamptz), + ('00000000-0000-4000-8000-0000000000a2', 'resolved', now()) + ) as v(id, status, resolved_at) + cross join lateral ( + select id, org_id from alert_rules where org_id = 'org_alerts_e2e' limit 1 + ) as rules`, + ) + const list = await harness.request("GET", "/v2/alerts/incidents?status=open", key.secret) + const open = Arr.findFirst( + list.body.data, + (incident: { status: string }) => incident.status === "open", + ) + const openId: string = open._tag === "Some" ? open.value.id : "" + expect(open._tag === "Some" && open.value.acknowledged_at).toBeNull() + + const acked = await harness.request("POST", `/v2/alerts/incidents/${openId}/acknowledge`, key.secret) + expect(acked.status).toBe(200) + expect(acked.body.status).toBe("open") + expect(acked.body.acknowledged_at).toEqual(expect.any(String)) + // An API key is named by its label. + expect(acked.body.acknowledged_by).toEqual({ user_id: "user_alerts_e2e", name: "alerts-test" }) + expect(acked.body.acknowledged_via).toBe("api") + + const unacknowledgedOnly = await harness.request( + "GET", + "/v2/alerts/incidents?acknowledged=false", + key.secret, + ) + expect(unacknowledgedOnly.body.data).toEqual([]) + const acknowledgedOnly = await harness.request( + "GET", + "/v2/alerts/incidents?acknowledged=true", + key.secret, + ) + expect(Arr.map(acknowledgedOnly.body.data, (incident: { id: string }) => incident.id)).toEqual([ + openId, + ]) + + // alerts:read can see the history but not change it. + const denied = await harness.request( + "POST", + `/v2/alerts/incidents/${openId}/unacknowledge`, + readOnly.secret, + ) + expect(denied.status).toBe(403) + + const unacked = await harness.request( + "POST", + `/v2/alerts/incidents/${openId}/unacknowledge`, + key.secret, + ) + expect(unacked.status).toBe(200) + expect(unacked.body.acknowledged_at).toBeNull() + expect(unacked.body.acknowledged_by).toBeNull() + + const events = await harness.request("GET", `/v2/alerts/incidents/${openId}/events`, readOnly.secret) + expect(events.status).toBe(200) + expect(Arr.map(events.body.data, (event: { type: string }) => event.type)).toEqual([ + "acknowledged", + "unacknowledged", + ]) + expect(events.body.data[0]).toMatchObject({ + object: "alert_incident_event", + incident_id: openId, + actor: { user_id: "user_alerts_e2e", name: "alerts-test" }, + source: "api", + }) + expect(events.body.data[0].id).toMatch(/^evt_/) + + const all = await harness.request("GET", "/v2/alerts/incidents", key.secret) + const resolved = Arr.findFirst( + all.body.data, + (incident: { status: string }) => incident.status === "resolved", + ) + const resolvedId: string = resolved._tag === "Some" ? resolved.value.id : "" + const conflict = await harness.request( + "POST", + `/v2/alerts/incidents/${resolvedId}/acknowledge`, + key.secret, + ) + expect(conflict.status).toBe(409) + expect(conflict.body.error).toMatchObject({ code: "alert_incident_resolved" }) + + const missing = await harness.request( + "POST", + "/v2/alerts/incidents/inc_tC4d9V79DCDzgbGKhAnff9/acknowledge", + key.secret, + ) + expect(missing.status).toBe(404) + + await harness.dispose() + }) + it("enforces the alerts scope family and rejects malformed ids", async () => { const harness = makeHarness() const readOnly = await harness.bootstrapKey(["alerts:read"]) diff --git a/apps/api/src/routes/v2/v2-test-support.ts b/apps/api/src/routes/v2/v2-test-support.ts index d6f0dd57d6..33029d908c 100644 --- a/apps/api/src/routes/v2/v2-test-support.ts +++ b/apps/api/src/routes/v2/v2-test-support.ts @@ -5,6 +5,7 @@ import { EdgeCacheService, MemoryCacheBackendLive } from "@maple/cache" import { AlertsService } from "@maple/backend/services/alerts/AlertsService" import { AlertDestinationsService } from "@maple/backend/services/alerts/AlertDestinationsService" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" +import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" import { AnomalyDetectionService } from "@maple/backend/services/alerts/AnomalyDetectionService" import { ErrorsService } from "@maple/backend/services/errors/ErrorsService" @@ -181,7 +182,11 @@ const v2GroupLayersExceptOnboardingChecklist = ( HttpV2AlertDeliveriesLive, HttpV2AlertRulesLive, HttpV2AlertDestinationsLive, - HttpV2AlertIncidentsLive, + // Real ack service: it needs only the Database. API keys are named by their label, so the + // empty member directory is never asked for one. + HttpV2AlertIncidentsLive.pipe( + Layer.provide(Layer.merge(AlertIncidentAckService.layer, OrgMembersServiceStubLayer)), + ), HttpV2IngestKeysLive, HttpV2SourcesLive, HttpV2ErrorIssuesLive, diff --git a/apps/api/src/runtime/service-graph.ts b/apps/api/src/runtime/service-graph.ts index 53c2b06b83..774172532c 100644 --- a/apps/api/src/runtime/service-graph.ts +++ b/apps/api/src/runtime/service-graph.ts @@ -9,6 +9,7 @@ import { Env } from "@maple/backend/platform/Env" import { AlertsService } from "@maple/backend/services/alerts/AlertsService" import { AlertDestinationsService } from "@maple/backend/services/alerts/AlertDestinationsService" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" +import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" import { AnomalyDetectionService } from "@maple/backend/services/alerts/AnomalyDetectionService" @@ -175,6 +176,7 @@ export const HttpServicesLive = Layer.mergeAll( DashboardWidgetDataService.layer, AlertDestinationsService.layer, AlertReadModelsService.layer, + AlertIncidentAckService.layer, AlertRulesService.layer, AlertsService.layer, AnomalyDetectionService.layer, diff --git a/apps/web/src/lab/alerts-fixture.ts b/apps/web/src/lab/alerts-fixture.ts index 6931236499..9e7ccfa707 100644 --- a/apps/web/src/lab/alerts-fixture.ts +++ b/apps/web/src/lab/alerts-fixture.ts @@ -542,6 +542,10 @@ const incidentFrom = (spec: IncidentSpec, rule: AlertRuleDocument, nowMs: number lastNotifiedAt: iso(nowMs - (open ? 8 * MIN : (spec.endAgo ?? 0))), holdReason: spec.held ? "volume_collapsed" : null, heldSince: spec.held ? iso(nowMs - 58 * MIN) : null, + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, errorIssueId: null, }) } diff --git a/packages/alerting-core/src/index.ts b/packages/alerting-core/src/index.ts index d19f935544..8c4620679a 100644 --- a/packages/alerting-core/src/index.ts +++ b/packages/alerting-core/src/index.ts @@ -173,6 +173,8 @@ export interface AlertLifecycleIncident { readonly firstTriggeredAtMs: number readonly lastNotifiedAtMs: number | null readonly lastDeliveredEventType: AlertEventType | null + /** Someone is on it: reminders and de-escalation notices stay quiet. */ + readonly acknowledged?: boolean } export type AlertIncidentTransition = @@ -306,6 +308,8 @@ export interface AlertLifecyclePlan { readonly inheritedNotificationAtMs: number | null /** Whether the host must advance lastNotifiedAt before queueing the event. */ readonly advanceNotificationAnchor: boolean + /** Whether the host must clear the incident's acknowledgement (an escalation pages again). */ + readonly clearAcknowledgement: boolean } export interface AlertDeliveryRetryPolicy { @@ -358,6 +362,7 @@ const noTransition = ( hold, inheritedNotificationAtMs: null, advanceNotificationAnchor: false, + clearAcknowledgement: false, }) const resolutionPlan = ( @@ -375,6 +380,7 @@ const resolutionPlan = ( hold: null, inheritedNotificationAtMs: null, advanceNotificationAnchor: false, + clearAcknowledgement: false, } } @@ -439,6 +445,7 @@ export const planAlertLifecycle = (input: AlertLifecycleInput): Effect.Effect { + const tiers = [warnAt(10), critAt(20)] + const tieredPolicy = { + consecutiveBreachesRequired: 1, + breachWindow: 1, + consecutiveHealthyRequired: 1, + renotifyIntervalMinutes: 10, + severities: ["warning" as const, "critical" as const], + } + const openAt = (severity: "warning" | "critical", acknowledged: boolean): AlertLifecycleIncident => ({ + severity, + firstTriggeredAtMs: 0, + lastNotifiedAtMs: 0, + lastDeliveredEventType: "trigger", + acknowledged, + }) + const tick = (value: number, incident: AlertLifecycleIncident) => + plan({ + policy: tieredPolicy, + evaluation: evaluate(tiers, value), + state: { consecutiveBreaches: 1, consecutiveHealthy: 0 }, + openIncident: incident, + // Well past the 10-minute reminder interval. + nowMs: 60 * 60_000, + }) + + it("sends no reminder and keeps the reminder clock while acknowledged", () => { + expect(tick(15, openAt("warning", false))).toMatchObject({ + eventType: "renotify", + advanceNotificationAnchor: true, + }) + expect(tick(15, openAt("warning", true))).toMatchObject({ + transition: "continued", + eventType: null, + advanceNotificationAnchor: false, + clearAcknowledgement: false, + }) + }) + + it("pages an escalation despite the ack, and asks the host to clear it", () => { + expect(tick(25, openAt("warning", true))).toMatchObject({ + transition: "escalated", + eventType: "trigger", + advanceNotificationAnchor: true, + clearAcknowledgement: true, + }) + expect(tick(25, openAt("warning", false))).toMatchObject({ clearAcknowledgement: false }) + }) + + it("de-escalates quietly while acknowledged", () => { + expect(tick(15, openAt("critical", true))).toMatchObject({ + transition: "deescalated", + severity: "warning", + eventType: null, + advanceNotificationAnchor: false, + clearAcknowledgement: false, + }) + }) + + it("still sends the all-clear when an acknowledged incident resolves", () => { + expect(tick(5, openAt("warning", true))).toMatchObject({ + transition: "resolved", + eventType: "resolve", + }) + }) +}) diff --git a/packages/backend/src/services/alerts/AlertIncidentAckService.test.ts b/packages/backend/src/services/alerts/AlertIncidentAckService.test.ts new file mode 100644 index 0000000000..72a81e5c21 --- /dev/null +++ b/packages/backend/src/services/alerts/AlertIncidentAckService.test.ts @@ -0,0 +1,112 @@ +import { afterEach, assert, describe, it } from "@effect/vitest" +import { AlertIncidentId, AlertRuleId, OrgId, UserId } from "@maple/domain/http" +import { Duration, Effect, Layer, Schema, Array as Arr } from "effect" +import { TestClock } from "effect/testing" +import { cleanupTestDbs, createTestDb, executeSql, type TestDb } from "@maple/backend/platform/test-pglite" +import { AlertIncidentAckService, type AlertIncidentActor } from "./AlertIncidentAckService" + +const asOrgId = Schema.decodeUnknownSync(OrgId) +const asRuleId = Schema.decodeUnknownSync(AlertRuleId) +const asIncidentId = Schema.decodeUnknownSync(AlertIncidentId) +const asUserId = Schema.decodeUnknownSync(UserId) + +const ORG = asOrgId("org_alert_ack") +const OTHER_ORG = asOrgId("org_alert_ack_other") +const RULE = asRuleId("00000000-0000-4000-8000-000000000020") +const OPEN = asIncidentId("00000000-0000-4000-8000-000000000021") +const RESOLVED = asIncidentId("00000000-0000-4000-8000-000000000022") + +const dana: AlertIncidentActor = { userId: asUserId("user_dana"), name: "Dana Kim", source: "web" } +const slackClick: AlertIncidentActor = { userId: null, name: "sam", source: "slack" } + +const createdDbs: TestDb[] = [] +afterEach(() => cleanupTestDbs(createdDbs)) + +const makeLayer = (testDb: TestDb) => AlertIncidentAckService.layer.pipe(Layer.provide(testDb.layer)) + +const seed = (testDb: TestDb) => + Effect.promise(() => + executeSql( + testDb, + `INSERT INTO alert_incidents + (id, org_id, rule_id, incident_key, rule_name, group_key, signal_type, severity, status, + comparator, threshold, first_triggered_at, last_triggered_at, resolved_at, + dedupe_key, created_at, updated_at) + VALUES + ($1, $3, $4, 'ack-open', 'Checkout errors', '__total__', 'error_rate', 'critical', 'open', + 'gt', 5, '2026-11-06T09:00:00Z', '2026-11-06T09:10:00Z', null, 'd-open', now(), now()), + ($2, $3, $4, 'ack-resolved', 'Checkout errors', '__total__', 'error_rate', 'critical', 'resolved', + 'gt', 5, '2026-11-06T08:00:00Z', '2026-11-06T08:10:00Z', '2026-11-06T08:20:00Z', 'd-res', now(), now())`, + [OPEN, RESOLVED, ORG, RULE], + ), + ) + +describe("AlertIncidentAckService", () => { + it.effect("acknowledges, records who and where, and unacknowledges", () => { + const testDb = createTestDb(createdDbs) + return Effect.gen(function* () { + yield* seed(testDb) + const acks = yield* AlertIncidentAckService + + const acked = yield* acks.acknowledge(ORG, OPEN, dana) + assert.isTrue(acked.changed) + assert.strictEqual(acked.incident.status, "open") + assert.isNotNull(acked.incident.acknowledgedAt) + assert.strictEqual(acked.incident.acknowledgedByUserId, dana.userId) + assert.strictEqual(acked.incident.acknowledgedByName, "Dana Kim") + assert.strictEqual(acked.incident.acknowledgedVia, "web") + + yield* TestClock.adjust(Duration.minutes(3)) + const unacked = yield* acks.unacknowledge(ORG, OPEN, slackClick) + assert.isTrue(unacked.changed) + assert.isNull(unacked.incident.acknowledgedAt) + assert.isNull(unacked.incident.acknowledgedVia) + + const events = yield* acks.listEvents(ORG, OPEN) + assert.deepStrictEqual( + Arr.map(events, (event) => [event.type, event.actorName, event.source]), + [ + ["acknowledged", "Dana Kim", "web"], + ["unacknowledged", "sam", "slack"], + ], + ) + }).pipe(Effect.provide(makeLayer(testDb))) + }) + + it.effect("writes one event when the incident is already in the requested state", () => { + const testDb = createTestDb(createdDbs) + return Effect.gen(function* () { + yield* seed(testDb) + const acks = yield* AlertIncidentAckService + + yield* acks.acknowledge(ORG, OPEN, dana) + const again = yield* acks.acknowledge(ORG, OPEN, slackClick) + assert.isFalse(again.changed) + // The first acknowledger stays on record. + assert.strictEqual(again.incident.acknowledgedByName, "Dana Kim") + + const notAcked = yield* acks + .unacknowledge(ORG, OPEN, dana) + .pipe(Effect.andThen(acks.unacknowledge(ORG, OPEN, dana))) + assert.isFalse(notAcked.changed) + assert.lengthOf(yield* acks.listEvents(ORG, OPEN), 2) + }).pipe(Effect.provide(makeLayer(testDb))) + }) + + it.effect("refuses a resolved incident and hides other orgs' incidents", () => { + const testDb = createTestDb(createdDbs) + return Effect.gen(function* () { + yield* seed(testDb) + const acks = yield* AlertIncidentAckService + + const resolved = yield* Effect.flip(acks.acknowledge(ORG, RESOLVED, dana)) + assert.strictEqual(resolved._tag, "@maple/http/errors/AlertIncidentResolvedError") + + const foreign = yield* Effect.flip(acks.acknowledge(OTHER_ORG, OPEN, dana)) + assert.strictEqual(foreign._tag, "@maple/http/errors/AlertIncidentNotFoundError") + const foreignEvents = yield* Effect.flip(acks.listEvents(OTHER_ORG, OPEN)) + assert.strictEqual(foreignEvents._tag, "@maple/http/errors/AlertIncidentNotFoundError") + assert.lengthOf(yield* acks.listEvents(ORG, OPEN), 0) + }).pipe(Effect.provide(makeLayer(testDb))) + }) +}) diff --git a/packages/backend/src/services/alerts/AlertIncidentAckService.ts b/packages/backend/src/services/alerts/AlertIncidentAckService.ts new file mode 100644 index 0000000000..5655e744ad --- /dev/null +++ b/packages/backend/src/services/alerts/AlertIncidentAckService.ts @@ -0,0 +1,227 @@ +/** + * Acknowledging an open alert incident ("someone is on it"). The incident row carries the + * current acknowledgement, which the scheduler reads to hold reminders; `alert_incident_events` + * carries the history, which the timeline shows and on-call escalation reads. + */ +import { + AlertIncidentAckSource, + AlertIncidentEventDocument, + AlertIncidentEventId, + AlertIncidentEventType, + AlertIncidentNotFoundError, + AlertIncidentResolvedError, + type AlertIncidentDocument, + type AlertIncidentId, + type AlertPersistenceError, + type OrgId, + type UserId, +} from "@maple/domain/http" +import * as PG from "@maple-dev/effect-orm/postgres" +import { AlertIncidentEvents, AlertIncidents } from "@maple/db/tables" +import { Context, Effect, Layer, Option, Schema, Array as Arr } from "effect" +import { Database } from "@maple/backend/platform/DatabaseLive" +import { makeDbExecute } from "@maple/backend/platform/db-execute" +import { makePersistenceError } from "./alert-persistence" +import { AlertRuntime } from "./AlertRuntime" +import { rowToIncidentDocument } from "./AlertReadModelsService" + +/** Who acknowledged, and from where. A click from an unlinked chat account has a name and no user. */ +export interface AlertIncidentActor { + readonly userId: UserId | null + readonly name: string | null + readonly source: AlertIncidentAckSource +} + +export interface AlertIncidentAckResult { + readonly incident: AlertIncidentDocument + /** False when the incident was already in the requested state: nothing was written. */ + readonly changed: boolean +} + +type AckError = AlertIncidentNotFoundError | AlertIncidentResolvedError | AlertPersistenceError + +export interface AlertIncidentAckServiceApi { + readonly acknowledge: ( + orgId: OrgId, + incidentId: AlertIncidentId, + actor: AlertIncidentActor, + ) => Effect.Effect + readonly unacknowledge: ( + orgId: OrgId, + incidentId: AlertIncidentId, + actor: AlertIncidentActor, + ) => Effect.Effect + /** The incident's activity, oldest first. */ + readonly listEvents: ( + orgId: OrgId, + incidentId: AlertIncidentId, + ) => Effect.Effect< + ReadonlyArray, + AlertIncidentNotFoundError | AlertPersistenceError + > +} + +const decodeEventId = Schema.decodeUnknownSync(AlertIncidentEventId) +const decodeEventType = Schema.decodeUnknownOption(AlertIncidentEventType) +const decodeSource = Schema.decodeUnknownOption(AlertIncidentAckSource) +const decodeIso = Schema.decodeUnknownSync(AlertIncidentEventDocument.fields.createdAt) + +export class AlertIncidentAckService extends Context.Service< + AlertIncidentAckService, + AlertIncidentAckServiceApi +>()("@maple/backend/services/alerts/AlertIncidentAckService", { + make: Effect.gen(function* () { + const database = yield* Database + const dbExecute = makeDbExecute(database, "AlertIncidentAckService", makePersistenceError) + + const findIncident = (orgId: OrgId, incidentId: AlertIncidentId) => + dbExecute((db) => + db.run( + PG.from(AlertIncidents) + .select() + .where(($) => [$.orgId.eq(orgId), $.id.eq(incidentId)]) + .limit(1), + ), + ).pipe( + Effect.flatMap((rows) => + Effect.fromOption(Arr.head(rows)).pipe( + Effect.mapError( + () => + new AlertIncidentNotFoundError({ + message: `No such alert incident: '${incidentId}'`, + incidentId, + }), + ), + ), + ), + ) + + /** + * One guarded UPDATE per change, so two people clicking at once write one event: the loser + * matches no row and reads back the state the winner left. + */ + const setAcknowledged = (acknowledged: boolean) => + Effect.fn( + acknowledged + ? "AlertIncidentAckService.acknowledge" + : "AlertIncidentAckService.unacknowledge", + )(function* (orgId: OrgId, incidentId: AlertIncidentId, actor: AlertIncidentActor) { + yield* Effect.annotateCurrentSpan({ + orgId, + "maple.alert.incident_id": incidentId, + "maple.alert.ack_source": actor.source, + }) + const runtime = yield* AlertRuntime + const now = yield* runtime.now + const eventId = decodeEventId(runtime.makeUuid()) + const updated = yield* dbExecute((db) => + db.transaction( + Effect.gen(function* () { + const rows = yield* db.run( + PG.update(AlertIncidents) + .set( + acknowledged + ? { + acknowledgedAt: now, + acknowledgedByUserId: actor.userId, + acknowledgedByName: actor.name, + acknowledgedVia: actor.source, + updatedAt: now, + } + : { + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, + updatedAt: now, + }, + ) + .where(($) => [ + $.orgId.eq(orgId), + $.id.eq(incidentId), + $.status.eq("open"), + acknowledged + ? $.acknowledgedAt.isNull() + : $.acknowledgedAt.isNotNull(), + ]) + .returning(), + ) + if (rows.length > 0) { + yield* db.run( + PG.insertInto(AlertIncidentEvents).values({ + id: eventId, + orgId, + incidentId, + type: acknowledged ? "acknowledged" : "unacknowledged", + actorUserId: actor.userId, + actorName: actor.name, + source: actor.source, + createdAt: now, + }), + ) + } + return rows + }), + ), + ) + const row = Arr.head(updated) + if (Option.isSome(row)) { + yield* Effect.annotateCurrentSpan("maple.alert.ack_changed", true) + return { incident: rowToIncidentDocument(row.value), changed: true } + } + const current = yield* findIncident(orgId, incidentId) + if (current.status !== "open") { + return yield* new AlertIncidentResolvedError({ + message: `Alert incident '${incidentId}' is resolved; only an open incident can be ${acknowledged ? "acknowledged" : "unacknowledged"}.`, + incidentId, + }) + } + yield* Effect.annotateCurrentSpan("maple.alert.ack_changed", false) + return { incident: rowToIncidentDocument(current), changed: false } + }) + + const listEvents = Effect.fn("AlertIncidentAckService.listEvents")(function* ( + orgId: OrgId, + incidentId: AlertIncidentId, + ) { + yield* Effect.annotateCurrentSpan({ orgId, "maple.alert.incident_id": incidentId }) + yield* findIncident(orgId, incidentId) + const rows = yield* dbExecute((db) => + db.run( + PG.from(AlertIncidentEvents) + .select() + .where(($) => [$.orgId.eq(orgId), $.incidentId.eq(incidentId)]) + .orderBy(["createdAt", "asc"], ["id", "asc"]) + .limit(500), + ), + ) + // A type this build does not know (a newer writer) is skipped rather than failing the page. + return Arr.getSomes( + Arr.map(rows, (row) => + Option.map( + decodeEventType(row.type), + (type) => + new AlertIncidentEventDocument({ + id: row.id, + incidentId: row.incidentId, + type, + actorUserId: row.actorUserId, + actorName: row.actorName, + source: + row.source === null ? null : Option.getOrNull(decodeSource(row.source)), + createdAt: decodeIso(new Date(row.createdAt).toISOString()), + }), + ), + ), + ) + }) + + return { + acknowledge: setAcknowledged(true), + unacknowledge: setAcknowledged(false), + listEvents, + } satisfies AlertIncidentAckServiceApi + }), +}) { + static readonly layer = Layer.effect(this, this.make) +} diff --git a/packages/backend/src/services/alerts/AlertReadModelsService.ts b/packages/backend/src/services/alerts/AlertReadModelsService.ts index 7240673eb1..bfcf088c73 100644 --- a/packages/backend/src/services/alerts/AlertReadModelsService.ts +++ b/packages/backend/src/services/alerts/AlertReadModelsService.ts @@ -11,6 +11,7 @@ import { AlertEventType as AlertEventTypeSchema, AlertIncidentDocument, AlertIncidentsListResponse, + AlertIncidentAckSource, AlertIncidentHoldReason, AlertIncidentStatus, AlertIncidentTransition as AlertIncidentTransitionSchema, @@ -74,6 +75,8 @@ export interface AlertChecksSummary { export interface ListAlertIncidentsOptions { readonly status?: AlertIncidentStatus readonly ruleId?: AlertRuleId + /** Open incidents only, split by whether someone has acknowledged them. */ + readonly acknowledged?: boolean readonly limit?: number readonly offset?: number } @@ -145,6 +148,8 @@ const decodeAlertIncidentHoldReasonSync = Schema.decodeUnknownSync(AlertIncident const decodeAlertEventTypeSync = Schema.decodeUnknownSync(AlertEventTypeSchema) const decodeErrorIssueIdSync = Schema.decodeUnknownSync(AlertIncidentDocument.fields.errorIssueId) const decodeAlertDeliveryStatusSync = Schema.decodeUnknownSync(AlertDeliveryStatus) +// An unknown source (a newer writer) reads as none rather than failing the page. +const decodeAlertIncidentAckSource = Schema.decodeUnknownOption(AlertIncidentAckSource) type IsoDateTimeValue = Schema.Schema.Type @@ -153,7 +158,7 @@ const toIso = (value: number | null | undefined): IsoDateTimeValue | null => const makeValidationError = (message: string) => new AlertValidationError({ message, details: [] }) -const rowToIncidentDocument = (row: AlertIncidentRow) => +export const rowToIncidentDocument = (row: AlertIncidentRow) => new AlertIncidentDocument({ id: decodeAlertIncidentIdSync(row.id), ruleId: decodeAlertRuleIdSync(row.ruleId), @@ -177,6 +182,13 @@ const rowToIncidentDocument = (row: AlertIncidentRow) => lastNotifiedAt: toIso(row.lastNotifiedAt), holdReason: row.holdReason != null ? decodeAlertIncidentHoldReasonSync(row.holdReason) : null, heldSince: toIso(row.heldSince), + acknowledgedAt: toIso(row.acknowledgedAt), + acknowledgedByUserId: row.acknowledgedByUserId, + acknowledgedByName: row.acknowledgedByName, + acknowledgedVia: + row.acknowledgedVia === null + ? null + : Option.getOrNull(decodeAlertIncidentAckSource(row.acknowledgedVia)), errorIssueId: row.errorIssueId != null ? decodeErrorIssueIdSync(row.errorIssueId) : null, }) @@ -205,8 +217,9 @@ export class AlertReadModelsService extends Context.Service< orgId, ...(options.status !== undefined ? { status: options.status } : undefined), ...(options.ruleId !== undefined ? { ruleId: options.ruleId } : undefined), + ...(options.acknowledged !== undefined ? { acknowledged: options.acknowledged } : undefined), }) - const { status, ruleId } = options + const { status, ruleId, acknowledged } = options const rows = yield* dbExecute((db) => db.run( PG.from(AlertIncidents) @@ -215,6 +228,12 @@ export class AlertReadModelsService extends Context.Service< $.orgId.eq(orgId), status ? $.status.eq(status) : undefined, ruleId ? $.ruleId.eq(ruleId) : undefined, + acknowledged === undefined ? undefined : $.status.eq("open"), + acknowledged === undefined + ? undefined + : acknowledged + ? $.acknowledgedAt.isNotNull() + : $.acknowledgedAt.isNull(), ]) .orderBy(["lastTriggeredAt", "desc"], ["id", "desc"]) .limit(options.limit ?? 100) diff --git a/packages/backend/src/services/alerts/AlertsService.test.ts b/packages/backend/src/services/alerts/AlertsService.test.ts index a39e4e8391..0082f6aa93 100644 --- a/packages/backend/src/services/alerts/AlertsService.test.ts +++ b/packages/backend/src/services/alerts/AlertsService.test.ts @@ -1616,6 +1616,94 @@ describe("AlertsService", () => { }).pipe(Effect.provide(makeLayer(testDb, makeWarehouseStub(state), { fetch: failingFetch }))) }) + it.effect("holds reminders while an incident is acknowledged and clears the ack on resolve", () => { + const testDb = createTestDb(trackedDbs) + const breaching = tracesRow({ + count: 200, + avgDuration: 40, + p50Duration: 20, + p95Duration: 120, + p99Duration: 240, + errorRate: 10, + satisfiedCount: 180, + toleratingCount: 10, + apdexScore: 0.925, + }) + const state = { + tracesAggregateRows: [breaching] as ReadonlyArray>, + } + + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const alerts = yield* AlertsService + const orgId = asOrgId("org_ack_holds_renotify") + const userId = asUserId("user_ack_holds_renotify") + const destination = yield* createWebhookDestination(alerts, orgId, userId) + yield* alerts.createRule( + orgId, + userId, + adminRoles, + new AlertRuleUpsertRequest({ + name: "Checkout error rate", + severity: "critical", + enabled: true, + serviceNames: ["checkout"], + signalType: "error_rate", + comparator: "gt", + threshold: 5, + windowMinutes: 5, + minimumSampleCount: 10, + consecutiveBreachesRequired: 2, + consecutiveHealthyRequired: 2, + renotifyIntervalMinutes: 5, + destinationIds: [destination.id], + }), + ) + + // The trigger opens on the second tick; acknowledge it straight away. + for (let minute = 0; minute < 2; minute += 1) { + yield* alerts.runSchedulerTick() + yield* TestClock.adjust(Duration.minutes(1)) + } + yield* Effect.promise(() => + executeSql( + testDb, + `update alert_incidents set acknowledged_at = now(), acknowledged_by_user_id = $1, + acknowledged_by_name = 'Dana', acknowledged_via = 'web' where org_id = $2`, + [userId, orgId], + ), + ) + + // Well past the 5-minute reminder interval, still breaching. + for (let minute = 0; minute < 12; minute += 1) { + yield* alerts.runSchedulerTick() + yield* TestClock.adjust(Duration.minutes(1)) + } + const events = yield* alerts.listDeliveryEvents(orgId) + assert.deepStrictEqual( + Arr.dedupe(Arr.map(events.events, (event: { eventType: string }) => event.eventType)), + ["trigger"], + ) + + state.tracesAggregateRows = [{ ...breaching, errorRate: 1 }] + for (let minute = 0; minute < 3; minute += 1) { + yield* alerts.runSchedulerTick() + yield* TestClock.adjust(Duration.minutes(1)) + } + const [incident] = (yield* alerts.listIncidents(orgId)).incidents + assert.strictEqual(incident?.status, "resolved") + assert.isNull(incident?.acknowledgedAt) + assert.isNull(incident?.acknowledgedByName) + const afterResolve = yield* alerts.listDeliveryEvents(orgId) + assert.isTrue( + Arr.some( + afterResolve.events, + (event: { eventType: string }) => event.eventType === "resolve", + ), + ) + }).pipe(Effect.provide(makeLayer(testDb, makeWarehouseStub(state), { fetch: okFetch }))) + }) + it.effect("skips unchanged alert_rule_states writes and refreshes on the 5-minute heartbeat", () => { const testDb = createTestDb(trackedDbs) // Healthy from the start: errorRate 1 stays below the threshold of 5. diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 132b1b55e2..175e3506d4 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -39,6 +39,7 @@ import { AlertEventType as AlertEventTypeSchema, AlertForbiddenError, AlertIncidentDocument, + AlertIncidentEventId, AlertIncidentHoldReason, AlertIncidentStatus, type AlertDestinationNotFoundError, @@ -80,6 +81,7 @@ import { type AlertDeliveryEventRow, AlertDestinations, type AlertDestinationRow, + AlertIncidentEvents, AlertIncidents, type AlertIncidentRow, AlertRuleClaims, @@ -434,6 +436,15 @@ const StoredDeliveryPayloadSchema = Schema.StructWithRest( const decodeAlertRuleIdSync = Schema.decodeUnknownSync(AlertRuleDocument.fields.id) const decodeAlertIncidentIdSync = Schema.decodeUnknownSync(AlertIncidentDocument.fields.id) const decodeAlertDeliveryEventIdSync = Schema.decodeUnknownSync(AlertDeliveryEventDocument.fields.id) +const decodeAlertIncidentEventIdSync = Schema.decodeUnknownSync(AlertIncidentEventId) + +/** An incident that leaves `open` stops being acknowledged; its history stays in `alert_incident_events`. */ +const clearedAcknowledgement = { + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, +} as const const decodeIsoDateTimeStringSync = Schema.decodeUnknownSync(AlertDestinationDocument.fields.createdAt) const decodeAlertSeveritySync = Schema.decodeUnknownSync(AlertSeveritySchema) const decodeAlertSignalTypeSync = Schema.decodeUnknownSync(AlertSignalTypeSchema) @@ -3002,6 +3013,7 @@ export class AlertsService extends Context.Service [$.id.eq(openIncident.id), $.status.eq("open")]) .returning("id"), ), ) + if (refreshed.length > 0 && lifecycle.clearAcknowledgement) { + yield* dbExecute((db) => + db.run( + PG.insertInto(AlertIncidentEvents).values({ + id: decodeAlertIncidentEventIdSync(makeUuid()), + orgId: row.orgId, + incidentId: openIncident.id, + type: "ack_cleared", + actorUserId: null, + actorName: null, + source: null, + createdAt: timestamp, + }), + ), + ) + } if (refreshed.length === 0) { return { transition: "none" as const, @@ -3335,6 +3367,7 @@ export class AlertsService extends Context.Service [$.id.eq(openIncident.id), $.status.eq("open")]) @@ -3655,6 +3689,7 @@ export class AlertsService extends Context.Service [$.id.eq(incident.id), $.status.eq("open")]) @@ -3694,6 +3729,7 @@ export class AlertsService extends Context.Service [$.id.eq(incident.id), $.status.eq("open")]) @@ -3878,6 +3915,7 @@ export class AlertsService extends Context.Service = [ DashboardVersions, Dashboards, AlertDeliveryEvents, + // Before its incident: an event row references it. + AlertIncidentEvents, AlertIncidents, AlertRuleStates, AlertRules, diff --git a/packages/db/drizzle/20261105234724_alert_incident_ack/migration.sql b/packages/db/drizzle/20261105234724_alert_incident_ack/migration.sql new file mode 100644 index 0000000000..ab05cbd366 --- /dev/null +++ b/packages/db/drizzle/20261105234724_alert_incident_ack/migration.sql @@ -0,0 +1,30 @@ +-- metadata +CREATE TABLE IF NOT EXISTS "alert_incident_events" ( + "id" text NOT NULL, + "org_id" text NOT NULL, + "incident_id" text NOT NULL, + "type" text NOT NULL, + "actor_user_id" text, + "actor_name" text, + "source" text, + "created_at" timestamp with time zone NOT NULL, + CONSTRAINT "alert_incident_events_pkey" PRIMARY KEY ("id") +); +--> statement-breakpoint +-- metadata +ALTER TABLE "alert_incidents" ADD COLUMN IF NOT EXISTS "acknowledged_at" timestamp with time zone; +--> statement-breakpoint +-- metadata +ALTER TABLE "alert_incidents" ADD COLUMN IF NOT EXISTS "acknowledged_by_user_id" text; +--> statement-breakpoint +-- metadata +ALTER TABLE "alert_incidents" ADD COLUMN IF NOT EXISTS "acknowledged_by_name" text; +--> statement-breakpoint +-- metadata +ALTER TABLE "alert_incidents" ADD COLUMN IF NOT EXISTS "acknowledged_via" text; +--> statement-breakpoint +-- metadata +CREATE INDEX IF NOT EXISTS "alert_incident_events_incident_idx" ON "alert_incident_events" USING btree ("org_id", "incident_id", "created_at"); +--> statement-breakpoint +-- metadata +ALTER TABLE "alert_incident_events" ADD CONSTRAINT "alert_incident_events_incident_id_alert_incidents_id_fk" FOREIGN KEY ("incident_id") REFERENCES "alert_incidents" ("id") ON DELETE CASCADE; diff --git a/packages/db/drizzle/20261105234724_alert_incident_ack/snapshot.json b/packages/db/drizzle/20261105234724_alert_incident_ack/snapshot.json new file mode 100644 index 0000000000..654a895f1c --- /dev/null +++ b/packages/db/drizzle/20261105234724_alert_incident_ack/snapshot.json @@ -0,0 +1,18980 @@ +{ + "version": "1", + "dialect": "postgres", + "id": "735b86449ab62a2a8b8163dbb4278bca8e991f0dfafc0f3b7c2418ea6b2446b9", + "prevIds": [ + "5bec2645f5f8fd661f7ccf5046b1f82c678b054b45d008109dd6ea942fe6fac4" + ], + "entities": [ + { + "kind": "table", + "name": "actors", + "primaryKey": { + "name": "actors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "agent_feedback", + "primaryKey": { + "name": "agent_feedback_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "ai_triage_settings", + "primaryKey": { + "name": "ai_triage_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "alert_delivery_events", + "primaryKey": { + "name": "alert_delivery_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_destinations", + "primaryKey": { + "name": "alert_destinations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_incident_events", + "primaryKey": { + "name": "alert_incident_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_incidents", + "primaryKey": { + "name": "alert_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_linear_issues", + "primaryKey": { + "name": "alert_linear_issues_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_rule_claims", + "primaryKey": { + "name": "alert_rule_claims_pkey", + "columns": [ + "rule_id" + ] + } + }, + { + "kind": "table", + "name": "alert_rule_states", + "primaryKey": { + "name": "alert_rule_states_org_id_rule_id_group_key_pk", + "columns": [ + "org_id", + "rule_id", + "group_key" + ] + } + }, + { + "kind": "table", + "name": "alert_rules", + "primaryKey": { + "name": "alert_rules_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "anomaly_detector_settings", + "primaryKey": { + "name": "anomaly_detector_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "anomaly_detector_states", + "primaryKey": { + "name": "anomaly_detector_states_org_id_detector_key_pk", + "columns": [ + "org_id", + "detector_key" + ] + } + }, + { + "kind": "table", + "name": "anomaly_incidents", + "primaryKey": { + "name": "anomaly_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "api_keys", + "primaryKey": { + "name": "api_keys_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "aws_connections", + "primaryKey": { + "name": "aws_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cancellation_reviews", + "primaryKey": { + "name": "cancellation_reviews_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "chat_identities", + "primaryKey": { + "name": "chat_identities_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "chat_workspaces", + "primaryKey": { + "name": "chat_workspaces_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cli_device_authorizations", + "primaryKey": { + "name": "cli_device_authorizations_pkey", + "columns": [ + "device_code_hash" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_analytics_state", + "primaryKey": { + "name": "cloudflare_analytics_state_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_hyperdrive_configs", + "primaryKey": { + "name": "cloudflare_hyperdrive_configs_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_logpush_connectors", + "primaryKey": { + "name": "cloudflare_logpush_connectors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_zone_settings", + "primaryKey": { + "name": "cloudflare_zone_settings_pkey", + "columns": [ + "org_id", + "account_id" + ] + } + }, + { + "kind": "table", + "name": "dashboard_shares", + "primaryKey": { + "name": "dashboard_shares_org_id_id_pk", + "columns": [ + "org_id", + "id" + ] + } + }, + { + "kind": "table", + "name": "dashboard_versions", + "primaryKey": { + "name": "dashboard_versions_org_id_id_pk", + "columns": [ + "org_id", + "id" + ] + } + }, + { + "kind": "table", + "name": "dashboards", + "primaryKey": { + "name": "dashboards_org_id_id_pk", + "columns": [ + "org_id", + "id" + ] + } + }, + { + "kind": "table", + "name": "dependency_findings", + "primaryKey": { + "name": "dependency_findings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "dependency_scan_matches", + "primaryKey": { + "name": "dependency_scan_matches_pkey", + "columns": [ + "scan_id", + "lockfile_path", + "ecosystem", + "package_name", + "version", + "advisory_id" + ] + } + }, + { + "kind": "table", + "name": "dependency_scans", + "primaryKey": { + "name": "dependency_scans_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "device_credentials", + "primaryKey": { + "name": "device_credentials_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "device_proof_replays", + "primaryKey": { + "name": "device_proof_replays_pkey", + "columns": [ + "key" + ] + } + }, + { + "kind": "table", + "name": "digest_subscriptions", + "primaryKey": { + "name": "digest_subscriptions_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_fingerprint_candidates", + "primaryKey": { + "name": "error_fingerprint_candidates_org_id_fingerprint_hash_pk", + "columns": [ + "org_id", + "fingerprint_hash" + ] + } + }, + { + "kind": "table", + "name": "error_incidents", + "primaryKey": { + "name": "error_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_events", + "primaryKey": { + "name": "error_issue_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_fingerprints", + "primaryKey": { + "name": "error_issue_fingerprints_org_id_fingerprint_hash_pk", + "columns": [ + "org_id", + "fingerprint_hash" + ] + } + }, + { + "kind": "table", + "name": "error_issue_pull_requests", + "primaryKey": { + "name": "error_issue_pull_requests_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_states", + "primaryKey": { + "name": "error_issue_states_org_id_issue_id_pk", + "columns": [ + "org_id", + "issue_id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_verifications", + "primaryKey": { + "name": "error_issue_verifications_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issues", + "primaryKey": { + "name": "error_issues_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_notification_deliveries", + "primaryKey": { + "name": "error_notification_deliveries_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_notification_policies", + "primaryKey": { + "name": "error_notification_policies_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "error_tick_states", + "primaryKey": { + "name": "error_tick_states_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "gcp_connectors", + "primaryKey": { + "name": "gcp_connectors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "gcp_resources", + "primaryKey": { + "name": "gcp_resources_pkey", + "columns": [ + "connector_id", + "name" + ] + } + }, + { + "kind": "table", + "name": "investigations", + "primaryKey": { + "name": "investigations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "issue_escalation_policies", + "primaryKey": { + "name": "issue_escalation_policies_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "issue_escalations", + "primaryKey": { + "name": "issue_escalations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "live_activities", + "primaryKey": { + "name": "live_activities_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "mcp_oauth_authorizations", + "primaryKey": { + "name": "mcp_oauth_authorizations_pkey", + "columns": [ + "request_id_hash" + ] + } + }, + { + "kind": "table", + "name": "mcp_oauth_clients", + "primaryKey": { + "name": "mcp_oauth_clients_pkey", + "columns": [ + "client_id" + ] + } + }, + { + "kind": "table", + "name": "mcp_oauth_refresh_tokens", + "primaryKey": { + "name": "mcp_oauth_refresh_tokens_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "mobile_devices", + "primaryKey": { + "name": "mobile_devices_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "oauth_auth_states", + "primaryKey": { + "name": "oauth_auth_states_pkey", + "columns": [ + "state" + ] + } + }, + { + "kind": "table", + "name": "oauth_connections", + "primaryKey": { + "name": "oauth_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_billing_links", + "primaryKey": { + "name": "org_billing_links_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_clickhouse_schema_apply_runs", + "primaryKey": { + "name": "org_clickhouse_schema_apply_runs_org_id_pk", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_clickhouse_settings", + "primaryKey": { + "name": "org_clickhouse_settings_org_id_pk", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_attribute_mappings", + "primaryKey": { + "name": "org_ingest_attribute_mappings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_keys", + "primaryKey": { + "name": "org_ingest_keys_org_id_pk", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_rule_settings", + "primaryKey": { + "name": "org_ingest_rule_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_rule_stats", + "primaryKey": { + "name": "org_ingest_rule_stats_pkey", + "columns": [ + "org_id", + "rule_id", + "bucket", + "signal" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_rules", + "primaryKey": { + "name": "org_ingest_rules_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_sampling_policies", + "primaryKey": { + "name": "org_ingest_sampling_policies_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_onboarding_state", + "primaryKey": { + "name": "org_onboarding_state_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_openrouter_keys", + "primaryKey": { + "name": "org_openrouter_keys_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_recommendation_issues", + "primaryKey": { + "name": "org_recommendation_issues_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_support_channels", + "primaryKey": { + "name": "org_support_channels_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_connections", + "primaryKey": { + "name": "planetscale_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_databases", + "primaryKey": { + "name": "planetscale_databases_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_events", + "primaryKey": { + "name": "planetscale_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_issue_receipts", + "primaryKey": { + "name": "planetscale_issue_receipts_org_id_event_id_pk", + "columns": [ + "org_id", + "event_id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_poll_state", + "primaryKey": { + "name": "planetscale_poll_state_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_edits", + "primaryKey": { + "name": "pr_review_edits_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_finding_embeddings", + "primaryKey": { + "name": "pr_review_finding_embeddings_pkey", + "columns": [ + "finding_id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_findings", + "primaryKey": { + "name": "pr_review_findings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_merge_steps", + "primaryKey": { + "name": "pr_review_merge_steps_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_replies", + "primaryKey": { + "name": "pr_review_replies_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_settings", + "primaryKey": { + "name": "pr_review_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "pr_reviews", + "primaryKey": { + "name": "pr_reviews_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "railway_connections", + "primaryKey": { + "name": "railway_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "railway_environments", + "primaryKey": { + "name": "railway_environments_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "scrape_target_checks", + "primaryKey": { + "name": "scrape_target_checks_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "scrape_targets", + "primaryKey": { + "name": "scrape_targets_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_events", + "primaryKey": { + "name": "security_audit_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_findings", + "primaryKey": { + "name": "security_audit_findings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_org_reservations", + "primaryKey": { + "name": "security_audit_org_reservations_pkey", + "columns": [ + "run_id", + "agent_id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_org_settings", + "primaryKey": { + "name": "security_audit_org_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_run_findings", + "primaryKey": { + "name": "security_audit_run_findings_pkey", + "columns": [ + "run_id", + "fingerprint" + ] + } + }, + { + "kind": "table", + "name": "security_audit_runs", + "primaryKey": { + "name": "security_audit_runs_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_targets", + "primaryKey": { + "name": "security_audit_targets_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_units", + "primaryKey": { + "name": "security_audit_units_pkey", + "columns": [ + "run_id", + "coverage_id" + ] + } + }, + { + "kind": "table", + "name": "sources", + "primaryKey": { + "name": "sources_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_cleanups", + "primaryKey": { + "name": "status_page_cleanups_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_components", + "primaryKey": { + "name": "status_page_components_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_incident_components", + "primaryKey": { + "name": "status_page_incident_components_pk", + "columns": [ + "incident_id", + "component_id" + ] + } + }, + { + "kind": "table", + "name": "status_page_incident_updates", + "primaryKey": { + "name": "status_page_incident_updates_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_incidents", + "primaryKey": { + "name": "status_page_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_pages", + "primaryKey": { + "name": "status_pages_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "supabase_connections", + "primaryKey": { + "name": "supabase_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "supabase_project_insights", + "primaryKey": { + "name": "supabase_project_insights_pkey", + "columns": [ + "project_id" + ] + } + }, + { + "kind": "table", + "name": "supabase_projects", + "primaryKey": { + "name": "supabase_projects_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "uptime_monitor_daily", + "primaryKey": { + "name": "uptime_monitor_daily_pk", + "columns": [ + "monitor_id", + "day" + ] + } + }, + { + "kind": "table", + "name": "uptime_monitor_states", + "primaryKey": { + "name": "uptime_monitor_states_pkey", + "columns": [ + "monitor_id" + ] + } + }, + { + "kind": "table", + "name": "uptime_monitors", + "primaryKey": { + "name": "uptime_monitors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_commits", + "primaryKey": { + "name": "vcs_commits_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_installations", + "primaryKey": { + "name": "vcs_installations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_repositories", + "primaryKey": { + "name": "vcs_repositories_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_repository_branches", + "primaryKey": { + "name": "vcs_repository_branches_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "column", + "table": "actors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "type", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "user_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "agent_name", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "model", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "capabilities_json", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "created_by", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "last_active_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "impact", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "summary", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "reason", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "details", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "related_to", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_type", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_name", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_model", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_version", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "source", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "max_runs_per_day", + "position": 2, + "type": "integer", + "notNull": true, + "default": "20", + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "max_passes_per_day", + "position": 3, + "type": "integer", + "notNull": true, + "default": "90", + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "updated_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "updated_by", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "incident_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "rule_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "destination_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "delivery_key", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "event_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "attempt_number", + "position": 7, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "status", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "scheduled_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "claimed_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "claim_expires_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "claimed_by", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "attempted_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "provider_message", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "provider_reference", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "response_code", + "position": 16, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "error_message", + "position": 17, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "payload_json", + "position": 18, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "created_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "updated_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "enabled", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "config_json", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "secret_ciphertext", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "secret_iv", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "secret_tag", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "last_tested_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "last_test_error", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "consecutive_failures", + "position": 11, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "last_failure_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "disabled_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "disabled_reason", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "created_by", + "position": 17, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "updated_by", + "position": 18, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "incident_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "actor_user_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "actor_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "source", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "created_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "rule_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "incident_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "rule_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "group_key", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "signal_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "severity", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "peak_severity", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "comparator", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "threshold", + "position": 11, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "threshold_upper", + "position": 12, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "first_triggered_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_triggered_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "resolved_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_observed_value", + "position": 16, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_sample_count", + "position": 17, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_evaluated_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "dedupe_key", + "position": 19, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_delivered_event_type", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_notified_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "hold_reason", + "position": 22, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "held_since", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_by_user_id", + "position": 25, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_by_name", + "position": 26, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_via", + "position": 27, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "error_issue_id", + "position": 28, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "created_at", + "position": 29, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "updated_at", + "position": 30, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "destination_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "thread_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_team_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_issue_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_identifier", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_url", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "incident_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "rule_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "last_scheduled_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "consecutive_limit_failures", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "rule_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "group_key", + "position": 2, + "type": "text", + "notNull": true, + "default": "'__total__'", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "consecutive_breaches", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "consecutive_healthy", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "history_json", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_status", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_value", + "position": 7, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_sample_count", + "position": 8, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_evaluated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_error", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_error_kind", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "notes", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "notification_template_json", + "position": 4, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "enabled", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "severity", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "service_names_json", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "exclude_service_names_json", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "environments_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "tags_json", + "position": 10, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "signal_type", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "comparator", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "threshold", + "position": 13, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "threshold_upper", + "position": 14, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "thresholds_json", + "position": 15, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "window_minutes", + "position": 16, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "evaluation_interval_minutes", + "position": 17, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "slowed_interval_minutes", + "position": 18, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "slowed_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "minimum_sample_count", + "position": 20, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "consecutive_breaches_required", + "position": 21, + "type": "integer", + "notNull": true, + "default": "2", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "breaches_out_of", + "position": 22, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "consecutive_healthy_required", + "position": 23, + "type": "integer", + "notNull": true, + "default": "2", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "renotify_interval_minutes", + "position": 24, + "type": "integer", + "notNull": true, + "default": "30", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "apdex_threshold_ms", + "position": 25, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "query_builder_draft_json", + "position": 26, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "raw_query_sql", + "position": 27, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "group_by", + "position": 28, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "destination_ids_json", + "position": 29, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "query_spec_json", + "position": 30, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "reducer", + "position": 31, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "sample_count_strategy", + "position": 32, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "no_data_behavior", + "position": 33, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "last_scheduled_at", + "position": 34, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "created_at", + "position": 35, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "updated_at", + "position": 36, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "created_by", + "position": 37, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "updated_by", + "position": 38, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "sensitivity", + "position": 2, + "type": "text", + "notNull": true, + "default": "'normal'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "muted_signals_json", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "last_tick_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "updated_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "updated_by", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "detector_key", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "signal_type", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "service_name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "deployment_env", + "position": 4, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "fingerprint_hash", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "consecutive_breaches", + "position": 6, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "consecutive_healthy", + "position": 7, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_status", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_value", + "position": 9, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "baseline_median", + "position": 10, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_sample_count", + "position": 11, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_evaluated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "open_incident_id", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_resolved_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_incident_id", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "detector_key", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "signal_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "service_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "deployment_env", + "position": 5, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "fingerprint_hash", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "error_issue_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "status", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "severity", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "opened_value", + "position": 10, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "baseline_median", + "position": 11, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "baseline_sigma", + "position": 12, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "threshold_value", + "position": 13, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_observed_value", + "position": 14, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_sample_count", + "position": 15, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "first_triggered_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_triggered_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "resolved_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "resolve_reason", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "triage_status", + "position": 20, + "type": "text", + "notNull": true, + "default": "'none'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "dedupe_key", + "position": 21, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "fingerprints_json", + "position": 22, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "reopen_count", + "position": 23, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_reopened_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "created_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "updated_at", + "position": 26, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "description", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "key_hash", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "key_prefix", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "revoked", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "revoked_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "last_used_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "expires_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "metadata_json", + "position": 10, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "scopes", + "position": 11, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "kind", + "position": 12, + "type": "text", + "notNull": true, + "default": "'standard'", + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "created_by", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "created_by_email", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "account_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "modules", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "granted_modules", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "template_version", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "verified_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "last_checked_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "last_check_error", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "check_lease_until", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "metric_stream", + "position": 11, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "metric_stream_changed_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_ciphertext", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_iv", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_tag", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_hash", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_last_received_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_last_error", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "created_by", + "position": 19, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "created_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "updated_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "plan_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "subscription_started_at", + "position": 3, + "type": "bigint", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "canceled_at", + "position": 4, + "type": "bigint", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "snapshot_json", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "rule_reason", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "model_read", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "posted_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "connector", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "external_user_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "user_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "display_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "created_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "connector", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "external_workspace_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "settings", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "credentials_ciphertext", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "credentials_iv", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "credentials_tag", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "device_code_hash", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "user_code_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "device_name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_org_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_user_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_roles", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_user_email", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "api_key_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "token_ciphertext", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "token_iv", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "token_tag", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "denied_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "expires_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "account_id", + "position": 2, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "dataset", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "zone_id", + "position": 4, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "zone_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "enabled", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "watermark_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "backfill_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "settings_json", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "settings_fetched_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "quantiles_available", + "position": 11, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "discovered_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "live_scripts_json", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "grant_call_cap", + "position": 14, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "last_success_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "last_error", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "last_error_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "lease_until", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "created_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "updated_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "account_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "config_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_host", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_port", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_scheme", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_database", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_user", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "deleted_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "created_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "zone_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "service_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "dataset", + "position": 6, + "type": "text", + "notNull": true, + "default": "'http_requests'", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_ciphertext", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_iv", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_tag", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_hash", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "enabled", + "position": 11, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "last_received_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "last_error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_rotated_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "created_by", + "position": 17, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "updated_by", + "position": 18, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "account_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "watch_new_zones", + "position": 2, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "zone_overrides", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "updated_by", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "dashboard_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "widget_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "mode", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_hash", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_ciphertext", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_iv", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_tag", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_suffix", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "created_by", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "updated_by", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "revoked_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "dashboard_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "version_number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "snapshot_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "change_kind", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "change_summary", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "source_version_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "created_by", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "payload_json", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "created_by", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "updated_by", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "version", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "lockfile_path", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "ecosystem", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "package_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "advisory_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "aliases", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "severity", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "summary", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "versions", + "position": 10, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "fixed_in", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "relation", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status", + "position": 13, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "first_seen_scan_id", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "last_seen_scan_id", + "position": 15, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "first_seen_sha", + "position": 16, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "last_seen_sha", + "position": 17, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "fixed_scan_id", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "fixed_sha", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status_reason", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status_changed_by", + "position": 21, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status_changed_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "created_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "updated_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "scan_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "lockfile_path", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "ecosystem", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "package_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "version", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "relation", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "advisory_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "aliases", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "severity", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "summary", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "fixed_in", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "commit_sha", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "ref", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "trigger", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "default_branch_head", + "position": 6, + "type": "boolean", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "status", + "position": 7, + "type": "text", + "notNull": true, + "default": "'running'", + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "lockfiles", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "package_count", + "position": 9, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "vulnerable_count", + "position": 10, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "error", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "started_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "finished_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "installation_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "public_jwk", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "jkt", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "roles", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "scopes", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "attested", + "position": 8, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "attestation", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "last_used_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "expires_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "hard_expires_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "revoked_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_proof_replays", + "name": "key", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_proof_replays", + "name": "expires_at", + "position": 1, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "email", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "enabled", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "opted_out_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "day_of_week", + "position": 6, + "type": "integer", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "timezone", + "position": 7, + "type": "text", + "notNull": true, + "default": "'UTC'", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "send_hour", + "position": 8, + "type": "integer", + "notNull": true, + "default": "9", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "namespaces_json", + "position": 9, + "type": "text", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "environments_json", + "position": 10, + "type": "text", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "last_sent_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "last_attempted_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_enabled", + "position": 13, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_opted_out_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_last_sent_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_last_attempted_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "created_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "updated_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "fingerprint_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "service_name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "exception_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "exception_message", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "error_label", + "position": 5, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "top_frame", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "service_versions_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "occurrence_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "first_seen_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "last_seen_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "status", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "reason", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "first_triggered_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "last_triggered_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "resolved_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "occurrence_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "actor_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "type", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "from_state", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "to_state", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "payload_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "fingerprint_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "source", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "origin_issue_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "provider", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "external_repo_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "repo_full_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "number", + "position": 6, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "url", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "title", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "author_login", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "state", + "position": 10, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "merged_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "merge_commit_sha", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "link_source", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "linked_by_actor_id", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "issue_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "last_observed_occurrence_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "last_evaluated_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "open_incident_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "pull_request_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "status", + "position": 4, + "type": "text", + "notNull": true, + "default": "'waiting'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "merged_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "verify_after", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "baseline_versions_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "baseline_occurrence_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "baseline_rate_per_hour", + "position": 9, + "type": "double precision", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "investigation_id", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "verdict", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "verdict_note", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "post_merge_occurrence_count", + "position": 13, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "attempt", + "position": 14, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "kind", + "position": 2, + "type": "text", + "notNull": true, + "default": "'error'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "source_ref_json", + "position": 3, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "fingerprint_hash", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "fingerprint_version", + "position": 5, + "type": "integer", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "service_name", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "exception_type", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "exception_message", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "error_label", + "position": 9, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "top_frame", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "workflow_state", + "position": 11, + "type": "text", + "notNull": true, + "default": "'triage'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "priority", + "position": 12, + "type": "integer", + "notNull": true, + "default": "3", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "severity", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "severity_source", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "assigned_actor_id", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "lease_holder_actor_id", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "lease_expires_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "claimed_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "notes", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "first_seen_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "last_seen_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "occurrence_count", + "position": 22, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "resolved_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "resolved_by_actor_id", + "position": 24, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "last_resolved_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "last_regressed_at", + "position": 26, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "regression_count", + "position": 27, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "seen_versions_json", + "position": 28, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "resolved_versions_json", + "position": 29, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "snooze_until", + "position": 30, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "archived_at", + "position": 31, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "merged_into_issue_id", + "position": 32, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "created_at", + "position": 33, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "updated_at", + "position": 34, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "destination_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "delivery_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "payload_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "status", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "attempt_count", + "position": 6, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "scheduled_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "claimed_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "claim_expires_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "claimed_by", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "attempted_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "error_message", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "updated_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "destination_ids_json", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_first_seen", + "position": 3, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_regression", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_resolve", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_transition_in_review", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_transition_done", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_claim", + "position": 8, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "min_occurrence_count", + "position": 9, + "type": "integer", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "severity", + "position": 10, + "type": "text", + "notNull": true, + "default": "'warning'", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "updated_by", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "processed_through", + "position": 1, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "bootstrap_completed", + "position": 2, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "claim_token", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "claim_expires_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "scope_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "scope_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "project_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "logs_enabled", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "metrics_enabled", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_ciphertext", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_iv", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_tag", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_hash", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_received_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "applied_logs_enabled", + "position": 14, + "type": "boolean", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "applied_metrics_enabled", + "position": 15, + "type": "boolean", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "setup_reported_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "metrics_watermark_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_metrics_received_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_metrics_error", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "metrics_lease_until", + "position": 20, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "resources_synced_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_resources_error", + "position": 22, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "created_by", + "position": 23, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "created_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "updated_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "connector_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "asset_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "project_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "location", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "display_name", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "state", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "labels", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "details", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "resource_created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "resource_updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "last_seen_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "status", + "position": 2, + "type": "text", + "notNull": true, + "default": "'investigating'", + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "seeded_by", + "position": 3, + "type": "text", + "notNull": true, + "default": "'user'", + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "subject_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "snapshot_json", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "incident_kind", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "incident_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "issue_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "report_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "progress_json", + "position": 10, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "severity", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "confidence", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "model", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "input_tokens", + "position": 14, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "output_tokens", + "position": 15, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "error", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "started_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "autonomous_turns", + "position": 18, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "created_by", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "created_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "diagnosed_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "updated_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "rules_json", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "updated_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "updated_by", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "severity", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "source", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "reason", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "run_id", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "investigation_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "payload_json", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "delivery_results_json", + "position": 9, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "status", + "position": 10, + "type": "text", + "notNull": true, + "default": "'queued'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "attempts", + "position": 11, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "dedupe_key", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "processed_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "device_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "incident_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "activity_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "push_token", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "ended_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "ended_reason", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "updated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "request_id_hash", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "client_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "client_name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "redirect_uri", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "state", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "resource", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "scopes", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "code_challenge", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "authorization_code_hash", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_org_id", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_user_id", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_roles", + "position": 11, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_user_email", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "denied_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "used_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "created_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "expires_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "client_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "client_name", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "redirect_uris", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "client_uri", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "token_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "family_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "client_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "resource", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "scopes", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "org_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "user_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "roles", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "user_email", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "access_key_id", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "replaced_by_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "revoked_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "expires_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "family_expires_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "platform", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "token", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "environment", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "bundle_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "app_version", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "live_activity_start_token", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "device_name", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "preferences", + "position": 10, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "disabled_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "disabled_reason", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "last_pushed_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "last_seen_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "state", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "initiated_by_user_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "redirect_uri", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "return_to", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "code_verifier", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "github_installation_ids", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "expires_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "provider", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "external_user_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "external_user_email", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "external_account_name", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "granted_accounts_json", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "connected_by_user_id", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "scope", + "position": 9, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "access_token_ciphertext", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "access_token_iv", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "access_token_tag", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "refresh_token_ciphertext", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "refresh_token_iv", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "refresh_token_tag", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "expires_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "revoked_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "created_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "updated_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "billing_org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "linked_by", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "created_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "workflow_instance_id", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "status", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "phase", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "current_migration", + "position": 4, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "steps_total", + "position": 5, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "steps_done", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "applied_versions", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "skipped", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "error_message", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "started_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "finished_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "created_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "updated_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_url", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_user", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_password_ciphertext", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_password_iv", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_password_tag", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_database", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "sync_status", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "last_sync_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "last_sync_error", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "schema_version", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "created_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "created_by", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "updated_by", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "source_context", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "source_key", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "target_key", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "operation", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "enabled", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "updated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "public_key", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "public_key_hash", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_ciphertext", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_iv", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_tag", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_hash", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "public_rotated_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_rotated_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "created_by", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "updated_by", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_settings", + "name": "hash_salt", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_settings", + "name": "created_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "rule_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "bucket", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "signal", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "matched", + "position": 4, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "dropped", + "position": 5, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "dropped_bytes", + "position": 6, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "redacted_values", + "position": 7, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "enabled", + "position": 3, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "position", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "spec_json", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "created_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "updated_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "trace_sample_ratio", + "position": 1, + "type": "double precision", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "always_keep_error_spans", + "position": 2, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "always_keep_slow_spans_ms", + "position": 3, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "user_id", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "email", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "role", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "demo_data_requested", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "onboarding_completed_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "checklist_dismissed_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "first_data_received_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "welcome_email_sent_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "connect_nudge_email_sent_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "stalled_email_sent_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "activation_email_sent_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "reward_claimed_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "reward_reserved_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "updated_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_ciphertext", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_iv", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_tag", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_hint", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "updated_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "updated_by", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "number", + "position": 2, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "recommendation_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "kind", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "source_key", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "canonical_key", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "status", + "position": 7, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "usage_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "opened_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "resolved_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "slack_channel_id", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "slack_channel_name", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "reservation_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "reserved_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "created_by_user_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "created_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "updated_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "ps_organization", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "connected_by_user_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "scrape_target_id", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "webhook_secret_ciphertext", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "webhook_secret_iv", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "webhook_secret_tag", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "detected_permissions_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "last_inventory_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "last_inventory_error", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "created_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "updated_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "database_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "kind", + "position": 4, + "type": "text", + "notNull": true, + "default": "'mysql'", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "state", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "region", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "plan", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "branches_json", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_id", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_secret_ciphertext", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_secret_iv", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_secret_tag", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "deleted_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "database_id", + "position": 2, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "database_name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "branch_name", + "position": 4, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "category", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "event_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "state", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "external_id", + "position": 8, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "title", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "source", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "actor_login", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "url", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "payload_json", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "occurred_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_issue_receipts", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_issue_receipts", + "name": "event_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_issue_receipts", + "name": "processed_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "dataset", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "database_id", + "position": 3, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "enabled", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "watermark_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "last_success_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "last_error", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "last_error_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "lease_until", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "reply_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "seq", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "path", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "old_text", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "new_text", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "created_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "finding_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "model", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "embedding", + "position": 4, + "type": "real[]", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "review_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "handle", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "path", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "line", + "position": 7, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "category", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "severity", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "title", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "status", + "position": 11, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "comment_id", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "resolved_sha", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "reactions_up", + "position": 14, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "reactions_down", + "position": 15, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "created_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "updated_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "key", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "kind", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "title", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "source", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "path", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "done_by", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "done_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "reminded_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "first_review_id", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "last_review_id", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "comment_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "surface", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "thread_root_id", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "author_login", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "command", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "head_sha", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "status", + "position": 10, + "type": "text", + "notNull": true, + "default": "'queued'", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "session_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "reply_url", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "commit_sha", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "error", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "model", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "defaults", + "position": 2, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "updated_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "updated_by", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "head_sha", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "base_sha", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "url", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "title", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "author_login", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": "'queued'", + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "skip_reason", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "session_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "comment_attempt", + "position": 12, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "report_json", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "score", + "position": 14, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "check_run_url", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "comment_url", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "review_url", + "position": 17, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "publish_error", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "error", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "model", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "input_tokens", + "position": 21, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "output_tokens", + "position": 22, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "started_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "finished_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "merged_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "telemetry_json", + "position": 26, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "merge_commit_sha", + "position": 27, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "post_merge_status", + "position": 28, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "post_merge_after", + "position": 29, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "post_merge_json", + "position": 30, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "created_at", + "position": 31, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "updated_at", + "position": 32, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "token_ciphertext", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "token_iv", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "token_tag", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "workspace_names", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "connected_by_user_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "auth_failed_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "discovered_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "lease_until", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "last_success_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "last_error", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "last_error_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "updated_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "connection_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "project_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "project_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "environment_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "environment_name", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "services_json", + "position": 7, + "type": "text", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "enabled", + "position": 8, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "watermark_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "last_success_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "last_error", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "last_error_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "updated_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "id", + "position": 0, + "type": "integer", + "notNull": true, + "default": null, + "identity": "by default" + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "target_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "sub_target_key", + "position": 3, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "checked_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "error", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "duration_ms", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "samples_scraped", + "position": 7, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "samples_post_relabel", + "position": 8, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "service_name", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "url", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "target_type", + "position": 6, + "type": "text", + "notNull": true, + "default": "'prometheus'", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "discovery_config_json", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "scrape_interval_seconds", + "position": 8, + "type": "integer", + "notNull": true, + "default": "15", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "labels_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_type", + "position": 10, + "type": "text", + "notNull": true, + "default": "'none'", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "managed_by", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_credentials_ciphertext", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_credentials_iv", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_credentials_tag", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "enabled", + "position": 15, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "last_scrape_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "last_scrape_error", + "position": 17, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "created_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "updated_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "run_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "kind", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "role", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "agent_id", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "outcome", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "detail", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "input_tokens", + "position": 9, + "type": "bigint", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "output_tokens", + "position": 10, + "type": "bigint", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "target_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "fingerprint", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "verdict", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "overall_severity", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "estimated_severity", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "record", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status", + "position": 8, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "first_seen_run_id", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "last_seen_run_id", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "first_seen_ref", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "last_seen_ref", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "fixed_run_id", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status_reason", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status_changed_by", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status_changed_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "created_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "updated_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "run_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "agent_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "cost_usd", + "position": 3, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "sandbox_fee_usd", + "position": 4, + "type": "double precision", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "monthly_spend_limit_usd", + "position": 1, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "discovery_model", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "validation_model", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "updated_by", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "run_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "fingerprint", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "verdict", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "overall_severity", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "estimated_severity", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "record", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "target_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "source_ref", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "profile", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "scope_paths", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "budget", + "position": 7, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "metadata", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": "'in_progress'", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "incomplete_reason", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "architecture_md", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "reports", + "position": 12, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "execution_profile", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "invocations_spent", + "position": 14, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "input_tokens", + "position": 15, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "output_tokens", + "position": 16, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "cost_usd", + "position": 17, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "sandbox_usd", + "position": 18, + "type": "double precision", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "discovery_model", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "validation_model", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "workflow_instance_id", + "position": 21, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "started_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "finished_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "error", + "position": 24, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "repository", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "vcs_repository_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "branch", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "enabled", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "profile", + "position": 6, + "type": "text", + "notNull": true, + "default": "'deep'", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "scope_paths", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "slice_interval_hours", + "position": 8, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "revalidate_on_push", + "position": 9, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "budget_per_run", + "position": 10, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "monthly_invocation_budget", + "position": 11, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "discovery_model", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "validation_model", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "created_by", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "run_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "coverage_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "status", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "wave", + "position": 4, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "agent_id", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "unit", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "description", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "platform", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "kind", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "key_hash", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "key_prefix", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "allowed_signals", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "rate_limit_rps", + "position": 9, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "resource_attributes", + "position": 10, + "type": "jsonb", + "notNull": true, + "default": "'[]'::jsonb", + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "expires_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "paused_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "paused_until", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_used_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_rejected_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_rejection", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_poll_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_poll_ok_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_poll_error", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "disconnected_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "poll_resources", + "position": 21, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "rolled_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "created_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "updated_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "created_by", + "position": 25, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "updated_by", + "position": 26, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "page_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "slug", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "delete_snapshot", + "position": 4, + "type": "boolean", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "page_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "description", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "group_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "position", + "position": 6, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "monitor_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "manual_status", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "show_uptime", + "position": 9, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_components", + "name": "incident_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_components", + "name": "component_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_components", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "incident_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "status", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "body", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "source", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "created_by", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "overlay_entry_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "page_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "title", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "impact", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "status", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "published", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "alert_incident_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "scheduled_start", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "scheduled_end", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "started_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "resolved_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "created_by", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "updated_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "slug", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "description", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "website_url", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "logo_url", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "privacy_url", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "terms_url", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "theme", + "position": 9, + "type": "jsonb", + "notNull": true, + "default": "'{\"preset\":\"default\",\"appearance\":\"auto\",\"accent\":null,\"background\":null,\"text\":null,\"card\":null,\"border\":null,\"radius\":\"md\",\"font\":\"sans\"}'", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "show_powered_by", + "position": 10, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "auto_incidents", + "position": 11, + "type": "text", + "notNull": true, + "default": "'off'", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "break_glass_token_sha256", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "break_glass_token_suffix", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "draft", + "position": 14, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "revision", + "position": 15, + "type": "bigint", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "published_revision", + "position": 16, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "published_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "last_publish_error", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "published_hash", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "publish_attempted_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "created_by", + "position": 21, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "created_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "updated_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "supabase_organization_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "supabase_organization_slug", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "supabase_organization_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "connected_by_user_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "discovery_lease_until", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "last_discovery_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "last_discovery_error", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "project_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "advisors_json", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "top_queries_json", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "advisors_checked_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "queries_checked_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "advisors_error", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "queries_error", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "lease_until", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "connection_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "project_ref", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "region", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "status", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "metrics_enabled", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "scrape_target_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_enabled", + "position": 9, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_id", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_source_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_error", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drains_json", + "position": 13, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drains_checked_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_enabled", + "position": 15, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_watermark_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_boundary_ids", + "position": 17, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_lease_until", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_last_polled_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_error", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "last_seen_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "removed_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "created_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "updated_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "monitor_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "day", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "checks", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "failures", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "down_checks", + "position": 5, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "last_check_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "monitor_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "status", + "position": 2, + "type": "text", + "notNull": true, + "default": "'pending'", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "consecutive_failures", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "consecutive_successes", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "status_changed_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "last_check_json", + "position": 6, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "recent_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "last_ping_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "updated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "config_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "interval_seconds", + "position": 5, + "type": "integer", + "notNull": true, + "default": "60", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "failures_to_down", + "position": 6, + "type": "integer", + "notNull": true, + "default": "2", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "service_name", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "alert_rule_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "heartbeat_token", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "enabled", + "position": 10, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "created_by", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "created_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "updated_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "repository_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "sha", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "message", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_name", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_email", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_login", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_avatar_url", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "authored_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "committed_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "html_url", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "provider", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "external_installation_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "account_login", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "account_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "external_account_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "account_avatar_url", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "repository_selection", + "position": 9, + "type": "text", + "notNull": true, + "default": "'all'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "status", + "position": 10, + "type": "text", + "notNull": true, + "default": "'active'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "suspended_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "ci_traces_enabled", + "position": 12, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "repository_discovery", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "installed_by_user_id", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "installation_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "external_repo_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "owner", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "name", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "full_name", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "default_branch", + "position": 8, + "type": "text", + "notNull": true, + "default": "'main'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "tracked_branch", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "html_url", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "is_private", + "position": 11, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "is_archived", + "position": 12, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "status", + "position": 13, + "type": "text", + "notNull": true, + "default": "'active'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "sync_status", + "position": 14, + "type": "text", + "notNull": true, + "default": "'pending'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "last_synced_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "last_sync_error", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "pr_review_enabled", + "position": 17, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "pr_review_config", + "position": 18, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "created_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "updated_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "repository_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "is_default", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "head_sha", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "created_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "updated_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "index", + "table": "actors", + "name": "actors_org_agent_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"agent_name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "actors", + "name": "actors_org_type_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"type\"" + ], + "where": null + }, + { + "kind": "index", + "table": "actors", + "name": "actors_org_user_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "agent_feedback", + "name": "agent_feedback_kind_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"kind\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "agent_feedback", + "name": "agent_feedback_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_claim_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"claim_expires_at\"", + "\"scheduled_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_delivery_attempt_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"delivery_key\"", + "\"attempt_number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"scheduled_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_org_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_destinations", + "name": "alert_destinations_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_destinations", + "name": "alert_destinations_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_destinations", + "name": "alert_destinations_org_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incident_events", + "name": "alert_incident_events_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_incident_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"incident_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_open_group_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"rule_id\"", + "\"group_key\"" + ], + "where": "\"status\" = 'open'" + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"error_issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_rule_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"rule_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_linear_issues", + "name": "alert_linear_issues_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_linear_issues", + "name": "alert_linear_issues_thread_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"destination_id\"", + "\"thread_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rule_claims", + "name": "alert_rule_claims_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rule_states", + "name": "alert_rule_states_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rules", + "name": "alert_rules_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rules", + "name": "alert_rules_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rules", + "name": "alert_rules_org_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_detector_states", + "name": "anomaly_detector_states_evaluated_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"last_evaluated_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_detector_states", + "name": "anomaly_detector_states_open_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"open_incident_id\"" + ], + "where": "\"open_incident_id\" is not null" + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_open_detector_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"detector_key\"" + ], + "where": "\"status\" = 'open'" + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_detector_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"detector_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"error_issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_status_triggered_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"", + "\"last_triggered_at\"", + "\"id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_triggered_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"last_triggered_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "api_keys", + "name": "api_keys_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "api_keys", + "name": "api_keys_org_id_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "aws_connections", + "name": "aws_connections_org_account_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"account_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "aws_connections", + "name": "aws_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "aws_connections", + "name": "aws_connections_stream_secret_hash_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"stream_secret_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cancellation_reviews", + "name": "cancellation_reviews_subscription_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"plan_id\"", + "\"subscription_started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_identities", + "name": "chat_identities_org_connector_external_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"connector\"", + "\"external_user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_identities", + "name": "chat_identities_org_connector_user_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"connector\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_identities", + "name": "chat_identities_org_user_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_workspaces", + "name": "chat_workspaces_connector_external_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"connector\"", + "\"external_workspace_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_workspaces", + "name": "chat_workspaces_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cli_device_authorizations", + "name": "cli_device_authorizations_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cli_device_authorizations", + "name": "cli_device_authorizations_user_code_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"user_code_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_analytics_state", + "name": "cf_analytics_state_org_account_dataset_zone_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"account_id\"", + "\"dataset\"", + "\"zone_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_analytics_state", + "name": "cf_analytics_state_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_hyperdrive_configs", + "name": "cloudflare_hyperdrive_configs_org_config_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"config_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_hyperdrive_configs", + "name": "cloudflare_hyperdrive_configs_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_secret_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"secret_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_id_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_live_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"", + "coalesce(widget_id, '')" + ], + "where": "revoked_at is null" + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_org_dashboard_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_token_hash_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"token_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_versions", + "name": "dashboard_versions_org_dashboard_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"", + "\"version_number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_versions", + "name": "dashboard_versions_org_dashboard_version_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"", + "\"version_number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboards", + "name": "dashboards_org_name_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboards", + "name": "dashboards_org_updated_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"updated_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_findings", + "name": "dependency_findings_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"lockfile_path\"", + "\"ecosystem\"", + "\"package_name\"", + "\"advisory_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_findings", + "name": "dependency_findings_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_findings", + "name": "dependency_findings_org_updated_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"updated_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scan_matches", + "name": "dependency_scan_matches_org_scan_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"scan_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scans", + "name": "dependency_scans_org_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scans", + "name": "dependency_scans_repo_commit_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"commit_sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scans", + "name": "dependency_scans_repo_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "device_credentials", + "name": "device_credentials_jkt_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"jkt\"" + ], + "where": null + }, + { + "kind": "index", + "table": "device_credentials", + "name": "device_credentials_live_installation_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"installation_id\"" + ], + "where": "\"revoked_at\" IS NULL" + }, + { + "kind": "index", + "table": "device_credentials", + "name": "device_credentials_user_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "device_proof_replays", + "name": "device_proof_replays_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "digest_subscriptions", + "name": "digest_subscriptions_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "digest_subscriptions", + "name": "digest_subscriptions_org_user_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_fingerprint_candidates", + "name": "error_fingerprint_candidates_last_seen_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"last_seen_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_incidents", + "name": "error_incidents_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_incidents", + "name": "error_incidents_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"", + "\"last_triggered_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_events", + "name": "error_issue_events_actor_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"actor_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_events", + "name": "error_issue_events_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_events", + "name": "error_issue_events_type_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"type\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_fingerprints", + "name": "error_issue_fingerprints_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_pull_requests", + "name": "error_issue_pull_requests_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_pull_requests", + "name": "error_issue_pull_requests_issue_pr_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"", + "\"provider\"", + "\"repo_full_name\"", + "\"number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_pull_requests", + "name": "error_issue_pull_requests_repo_number_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"provider\"", + "\"repo_full_name\"", + "\"number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_verifications", + "name": "error_issue_verifications_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"verify_after\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_verifications", + "name": "error_issue_verifications_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_verifications", + "name": "error_issue_verifications_open_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": "\"status\" in ('waiting', 'running')" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_lease_expiry_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"lease_expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_archived_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"archived_at\"" + ], + "where": "\"archived_at\" is not null" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_assignee_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"assigned_actor_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_fp_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"fingerprint_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_fp_version_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"fingerprint_version\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_live_seen_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"last_seen_at\"", + "\"id\"" + ], + "where": "\"archived_at\" is null" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_merged_into_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"merged_into_issue_id\"" + ], + "where": "\"merged_into_issue_id\" is not null" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_severity_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"severity\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_workflow_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"workflow_state\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_notification_deliveries", + "name": "error_notification_deliveries_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"scheduled_at\"", + "\"claim_expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_notification_deliveries", + "name": "error_notification_deliveries_key_destination_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"delivery_key\"", + "\"destination_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_notification_deliveries", + "name": "error_notification_deliveries_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_tick_states", + "name": "error_tick_states_claim_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"claim_expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "gcp_connectors", + "name": "gcp_connectors_org_scope_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"scope_type\"", + "\"scope_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "gcp_connectors", + "name": "gcp_connectors_secret_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"secret_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "gcp_connectors", + "name": "gcp_connectors_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_incident_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_kind\"", + "\"incident_id\"" + ], + "where": "\"incident_id\" is not null" + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"" + ], + "where": null + }, + { + "kind": "index", + "table": "issue_escalations", + "name": "issue_escalations_dedupe_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"dedupe_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "issue_escalations", + "name": "issue_escalations_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "issue_escalations", + "name": "issue_escalations_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "live_activities", + "name": "live_activities_device_incident_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"device_id\"", + "\"incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "live_activities", + "name": "live_activities_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_authorizations", + "name": "mcp_oauth_authorizations_code_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"authorization_code_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_authorizations", + "name": "mcp_oauth_authorizations_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_refresh_tokens", + "name": "mcp_oauth_refresh_tokens_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_refresh_tokens", + "name": "mcp_oauth_refresh_tokens_family_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"family_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_refresh_tokens", + "name": "mcp_oauth_refresh_tokens_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"token_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mobile_devices", + "name": "mobile_devices_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mobile_devices", + "name": "mobile_devices_org_platform_token_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"platform\"", + "\"token\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mobile_devices", + "name": "mobile_devices_user_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_auth_states", + "name": "oauth_auth_states_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_connections", + "name": "oauth_connections_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_connections", + "name": "oauth_connections_org_provider_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"provider\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_connections", + "name": "oauth_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_billing_links", + "name": "org_billing_links_billing_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"billing_org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_attribute_mappings", + "name": "org_ingest_attribute_mappings_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_keys", + "name": "org_ingest_keys_private_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"private_key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_keys", + "name": "org_ingest_keys_public_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"public_key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_keys", + "name": "org_ingest_keys_public_key_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"public_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_rule_stats", + "name": "org_ingest_rule_stats_org_bucket_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"bucket\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_rules", + "name": "org_ingest_rules_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_recommendation_issues", + "name": "org_recommendation_issues_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_recommendation_issues", + "name": "org_recommendation_issues_org_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"recommendation_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_connections", + "name": "planetscale_connections_org_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_connections", + "name": "planetscale_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_databases", + "name": "planetscale_databases_org_db_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"database_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_databases", + "name": "planetscale_databases_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_events", + "name": "planetscale_events_dedupe_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"database_name\"", + "\"event_type\"", + "\"external_id\"", + "\"occurred_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_events", + "name": "planetscale_events_org_db_time_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"database_name\"", + "\"occurred_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_events", + "name": "planetscale_events_org_time_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"occurred_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_issue_receipts", + "name": "planetscale_issue_receipts_processed_at_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"processed_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_poll_state", + "name": "planetscale_poll_state_org_dataset_db_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dataset\"", + "\"database_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_poll_state", + "name": "planetscale_poll_state_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_edits", + "name": "pr_review_edits_reply_seq_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"reply_id\"", + "\"seq\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_finding_embeddings", + "name": "pr_review_finding_embeddings_org_model_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"model\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_findings", + "name": "pr_review_findings_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_findings", + "name": "pr_review_findings_pr_handle_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"", + "\"handle\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_merge_steps", + "name": "pr_review_merge_steps_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_merge_steps", + "name": "pr_review_merge_steps_pr_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"", + "\"key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_replies", + "name": "pr_review_replies_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_replies", + "name": "pr_review_replies_repo_comment_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"comment_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_post_merge_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"post_merge_after\"" + ], + "where": "\"post_merge_status\" = 'waiting'" + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_repo_number_head_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"", + "\"head_sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_repo_number_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_connections", + "name": "railway_connections_org_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_connections", + "name": "railway_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_environments", + "name": "railway_environments_connection_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"connection_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_environments", + "name": "railway_environments_org_env_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"environment_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_target_checks", + "name": "scrape_target_checks_target_checked_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"target_id\"", + "\"checked_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_targets", + "name": "scrape_targets_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_targets", + "name": "scrape_targets_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_targets", + "name": "scrape_targets_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_events", + "name": "security_audit_events_run_at_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"run_id\"", + "\"at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_findings", + "name": "security_audit_findings_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_findings", + "name": "security_audit_findings_target_fingerprint_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"target_id\"", + "\"fingerprint\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_org_reservations", + "name": "security_audit_org_reservations_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_runs", + "name": "security_audit_runs_one_running_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"target_id\"" + ], + "where": "\"finished_at\" IS NULL" + }, + { + "kind": "index", + "table": "security_audit_runs", + "name": "security_audit_runs_org_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_runs", + "name": "security_audit_runs_target_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"target_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_targets", + "name": "security_audit_targets_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_targets", + "name": "security_audit_targets_org_repo_branch_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"repository\"", + "\"branch\"" + ], + "where": null + }, + { + "kind": "index", + "table": "sources", + "name": "sources_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "sources", + "name": "sources_org_default_kind_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": "\"kind\" = 'default'" + }, + { + "kind": "index", + "table": "sources", + "name": "sources_org_id_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_cleanups", + "name": "status_page_cleanups_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_components", + "name": "status_page_components_monitor_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"monitor_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_components", + "name": "status_page_components_page_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"page_id\"", + "\"position\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incident_components", + "name": "status_page_incident_components_component_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"component_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incident_updates", + "name": "status_page_incident_updates_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"incident_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incident_updates", + "name": "status_page_incident_updates_overlay_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"overlay_entry_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incidents", + "name": "status_page_incidents_alert_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"alert_incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incidents", + "name": "status_page_incidents_page_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"page_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_pages", + "name": "status_pages_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_pages", + "name": "status_pages_slug_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"slug\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_pages", + "name": "status_pages_unpublished_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"revision\"", + "\"published_revision\"" + ], + "where": "revision > published_revision" + }, + { + "kind": "index", + "table": "supabase_connections", + "name": "supabase_connections_org_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_connections", + "name": "supabase_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_project_insights", + "name": "supabase_project_insights_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_projects", + "name": "supabase_projects_connection_ref_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"connection_id\"", + "\"project_ref\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_projects", + "name": "supabase_projects_log_drain_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"log_drain_source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_projects", + "name": "supabase_projects_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitor_daily", + "name": "uptime_monitor_daily_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitor_states", + "name": "uptime_monitor_states_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitors", + "name": "uptime_monitors_enabled_kind_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"enabled\"", + "\"kind\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitors", + "name": "uptime_monitors_heartbeat_token_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"heartbeat_token\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitors", + "name": "uptime_monitors_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_commits", + "name": "vcs_commits_org_sha_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_commits", + "name": "vcs_commits_repo_sha_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_installations", + "name": "vcs_installations_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_installations", + "name": "vcs_installations_provider_external_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"provider\"", + "\"external_installation_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_installations", + "name": "vcs_installations_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repositories", + "name": "vcs_repositories_installation_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"installation_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repositories", + "name": "vcs_repositories_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repositories", + "name": "vcs_repositories_org_repo_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"provider\"", + "\"external_repo_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repository_branches", + "name": "vcs_repository_branches_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repository_branches", + "name": "vcs_repository_branches_repo_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "foreign_key", + "table": "alert_incident_events", + "name": "alert_incident_events_incident_id_alert_incidents_id_fk", + "columns": [ + "incident_id" + ], + "foreignTable": "alert_incidents", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "alert_linear_issues", + "name": "alert_linear_issues_destination_id_alert_destinations_id_fk", + "columns": [ + "destination_id" + ], + "foreignTable": "alert_destinations", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "aws_connections", + "name": "aws_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "SET NULL", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "dashboard_shares", + "name": "dashboard_shares_dashboard_fk", + "columns": [ + "org_id", + "dashboard_id" + ], + "foreignTable": "dashboards", + "foreignColumns": [ + "org_id", + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "gcp_connectors", + "name": "gcp_connectors_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "gcp_resources", + "name": "gcp_resources_connector_id_gcp_connectors_id_fk", + "columns": [ + "connector_id" + ], + "foreignTable": "gcp_connectors", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "oauth_connections", + "name": "oauth_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "planetscale_connections", + "name": "planetscale_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "railway_connections", + "name": "railway_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "scrape_target_checks", + "name": "scrape_target_checks_target_id_scrape_targets_id_fk", + "columns": [ + "target_id" + ], + "foreignTable": "scrape_targets", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "scrape_targets", + "name": "scrape_targets_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_components", + "name": "status_page_components_monitor_id_uptime_monitors_id_fk", + "columns": [ + "monitor_id" + ], + "foreignTable": "uptime_monitors", + "foreignColumns": [ + "id" + ], + "onDelete": "SET NULL", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_components", + "name": "status_page_components_page_id_status_pages_id_fk", + "columns": [ + "page_id" + ], + "foreignTable": "status_pages", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incident_components", + "name": "status_page_incident_components_Bnqlt6wwKJty_fk", + "columns": [ + "component_id" + ], + "foreignTable": "status_page_components", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incident_components", + "name": "status_page_incident_components_tBn6mEz4FenY_fk", + "columns": [ + "incident_id" + ], + "foreignTable": "status_page_incidents", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incident_updates", + "name": "status_page_incident_updates_3LDDUAhznHPM_fk", + "columns": [ + "incident_id" + ], + "foreignTable": "status_page_incidents", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incidents", + "name": "status_page_incidents_page_id_status_pages_id_fk", + "columns": [ + "page_id" + ], + "foreignTable": "status_pages", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_connections", + "name": "supabase_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_project_insights", + "name": "supabase_project_insights_project_id_supabase_projects_id_fk", + "columns": [ + "project_id" + ], + "foreignTable": "supabase_projects", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_projects", + "name": "supabase_projects_connection_id_supabase_connections_id_fk", + "columns": [ + "connection_id" + ], + "foreignTable": "supabase_connections", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_projects", + "name": "supabase_projects_log_drain_source_id_sources_id_fk", + "columns": [ + "log_drain_source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "SET NULL", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "uptime_monitor_daily", + "name": "uptime_monitor_daily_monitor_id_uptime_monitors_id_fk", + "columns": [ + "monitor_id" + ], + "foreignTable": "uptime_monitors", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "uptime_monitor_states", + "name": "uptime_monitor_states_monitor_id_uptime_monitors_id_fk", + "columns": [ + "monitor_id" + ], + "foreignTable": "uptime_monitors", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "vcs_installations", + "name": "vcs_installations_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + } + ] +} diff --git a/packages/db/src/tables/alerts.ts b/packages/db/src/tables/alerts.ts index f1022d93e2..e0b2a069c6 100644 --- a/packages/db/src/tables/alerts.ts +++ b/packages/db/src/tables/alerts.ts @@ -2,10 +2,12 @@ import * as PG from "@maple-dev/effect-orm/postgres" import { AlertDeliveryEventId, AlertDestinationId, + AlertIncidentEventId, AlertIncidentId, AlertRuleId, ErrorIssueId, OrgId, + UserId, } from "@maple/domain/primitives" import { Schema } from "effect" @@ -212,6 +214,15 @@ const alertIncidentsColumns = { // flowing. Both are null once the incident is firing or resolved. holdReason: PG.column(PG.nullable(PG.text), { name: "hold_reason" }), heldSince: PG.column(PG.nullable(PG.timestamptzMillis), { name: "held_since" }), + // Set while an open incident is acknowledged ("someone is on it"): the + // scheduler sends no reminders until it is unacknowledged, escalates to a + // higher severity, or resolves. All four are null otherwise. + acknowledgedAt: PG.column(PG.nullable(PG.timestamptzMillis), { name: "acknowledged_at" }), + acknowledgedByUserId: PG.column(PG.nullable(PG.brand(PG.text, UserId)), { + name: "acknowledged_by_user_id", + }), + acknowledgedByName: PG.column(PG.nullable(PG.text), { name: "acknowledged_by_name" }), + acknowledgedVia: PG.column(PG.nullable(PG.text), { name: "acknowledged_via" }), // Issue-hub link: the error_issues row (kind="alert") this incident // feeds, mirroring anomalyIncidents.errorIssueId. errorIssueId: PG.column(PG.nullable(PG.brand(PG.text, ErrorIssueId)), { name: "error_issue_id" }), @@ -245,6 +256,36 @@ export const AlertIncidents = PG.table("alert_incidents", { tenantColumn: "orgId", }) +/** + * Append-only activity on an alert incident: acknowledgements, unacknowledgements and + * the scheduler clearing an ack on escalation. The incident row holds the current ack; + * this holds its history, and is what on-call escalation reads to stop escalating. + */ +export const AlertIncidentEvents = PG.table("alert_incident_events", { + columns: { + id: PG.brand(PG.text, AlertIncidentEventId), + orgId: PG.column(PG.brand(PG.text, OrgId), { name: "org_id" }), + incidentId: PG.column(PG.brand(PG.text, AlertIncidentId), { name: "incident_id" }), + type: PG.text, + actorUserId: PG.column(PG.nullable(PG.brand(PG.text, UserId)), { name: "actor_user_id" }), + actorName: PG.column(PG.nullable(PG.text), { name: "actor_name" }), + /** Null for scheduler-written events. */ + source: PG.nullable(PG.text), + createdAt: PG.column(PG.timestamptzMillis, { name: "created_at" }), + }, + primaryKey: ["id"], + indexes: [PG.index("alert_incident_events_incident_idx", ["orgId", "incidentId", "createdAt"])], + foreignKeys: [ + PG.foreignKey({ + columns: ["incidentId"], + references: AlertIncidents, + foreignColumns: ["id"], + onDelete: "cascade", + }), + ], + tenantColumn: "orgId", +}) + export const AlertDeliveryEvents = PG.table("alert_delivery_events", { columns: { id: PG.brand(PG.text, AlertDeliveryEventId), @@ -324,4 +365,5 @@ export type AlertRuleRow = PG.SelectRowOf export type AlertRuleStateRow = PG.SelectRowOf export type AlertIncidentRow = PG.SelectRowOf export type AlertDeliveryEventRow = PG.SelectRowOf +export type AlertIncidentEventRow = PG.SelectRowOf export type AlertLinearIssueRow = PG.SelectRowOf diff --git a/packages/db/src/tables/index.ts b/packages/db/src/tables/index.ts index ee1962ff46..ac85205803 100644 --- a/packages/db/src/tables/index.ts +++ b/packages/db/src/tables/index.ts @@ -7,6 +7,7 @@ import { AlertRuleClaims, AlertRuleStates, AlertIncidents, + AlertIncidentEvents, AlertDeliveryEvents, AlertLinearIssues, } from "./alerts" @@ -162,6 +163,7 @@ export const allTables: ReadonlyArray> = [ AlertRuleClaims, AlertRuleStates, AlertIncidents, + AlertIncidentEvents, AlertDeliveryEvents, AlertLinearIssues, AnomalyDetectorSettings, diff --git a/packages/domain/src/http/alerts.ts b/packages/domain/src/http/alerts.ts index 419fb3f8b0..57af9712db 100644 --- a/packages/domain/src/http/alerts.ts +++ b/packages/domain/src/http/alerts.ts @@ -3,6 +3,7 @@ import { QueryEngineAlertReducer, QueryEngineNoDataBehavior } from "../query-eng import { AlertDeliveryEventId, AlertDestinationId, + AlertIncidentEventId, AlertIncidentId, AlertRuleId, ChatConnectorId, @@ -127,6 +128,27 @@ export const AlertIncidentHoldReason = Schema.Literals([ }) export type AlertIncidentHoldReason = Schema.Schema.Type +/** Where an acknowledgement came from. */ +export const AlertIncidentAckSource = Schema.Literals(["web", "api", "mcp", "slack", "discord"]).annotate({ + identifier: "@maple/AlertIncidentAckSource", + title: "Alert Incident Acknowledgement Source", +}) +export type AlertIncidentAckSource = Schema.Schema.Type + +/** + * The incident activity log. `ack_cleared` is written by the scheduler when an + * acknowledged incident escalates to a higher severity and pages again. + */ +export const AlertIncidentEventType = Schema.Literals([ + "acknowledged", + "unacknowledged", + "ack_cleared", +]).annotate({ + identifier: "@maple/AlertIncidentEventType", + title: "Alert Incident Event Type", +}) +export type AlertIncidentEventType = Schema.Schema.Type + export const AlertEventType = Schema.Literals(["trigger", "resolve", "renotify", "test"]).annotate({ identifier: "@maple/AlertEventType", title: "Alert Event Type", @@ -875,9 +897,32 @@ export class AlertIncidentDocument extends Schema.Class(" /** Set while the incident is open but waiting on telemetry; see {@link AlertIncidentHoldReason}. */ holdReason: Schema.NullOr(AlertIncidentHoldReason), heldSince: Schema.NullOr(IsoDateTimeString), + /** + * Set while an open incident is acknowledged: reminders pause until it is + * unacknowledged, escalates or resolves. All null otherwise. + */ + acknowledgedAt: Schema.NullOr(IsoDateTimeString), + /** The Maple user who acknowledged; null for an unlinked chat account. */ + acknowledgedByUserId: Schema.NullOr(UserId), + /** Display name of whoever acknowledged, as the source reported it. */ + acknowledgedByName: Schema.NullOr(Schema.String), + acknowledgedVia: Schema.NullOr(AlertIncidentAckSource), errorIssueId: Schema.NullOr(ErrorIssueId), }) {} +export class AlertIncidentEventDocument extends Schema.Class( + "AlertIncidentEventDocument", +)({ + id: AlertIncidentEventId, + incidentId: AlertIncidentId, + type: AlertIncidentEventType, + actorUserId: Schema.NullOr(UserId), + actorName: Schema.NullOr(Schema.String), + /** Null for scheduler-written events. */ + source: Schema.NullOr(AlertIncidentAckSource), + createdAt: IsoDateTimeString, +}) {} + export class AlertIncidentsListResponse extends Schema.Class( "AlertIncidentsListResponse", )({ @@ -1165,6 +1210,20 @@ export class AlertIncidentNotFoundError extends HttpTaggedError()( + "@maple/http/errors/AlertIncidentResolvedError", + { message: Schema.String, incidentId: AlertIncidentId }, + { + status: 409, + code: "alert_incident_resolved", + title: "Alert incident already resolved", + retry: "never", + recovery: "none", + exposure: "public_message", + }, +) {} + export type AlertNotFoundError = | AlertRuleNotFoundError | AlertDestinationNotFoundError diff --git a/packages/domain/src/http/v2/alert-incidents.ts b/packages/domain/src/http/v2/alert-incidents.ts index cb6c880c35..5dd1e7e177 100644 --- a/packages/domain/src/http/v2/alert-incidents.ts +++ b/packages/domain/src/http/v2/alert-incidents.ts @@ -1,9 +1,11 @@ import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/http-api" import { Schema } from "effect" -import { AlertIncidentNotFoundError, AlertPersistenceError } from "../alerts" +import { AlertIncidentNotFoundError, AlertIncidentResolvedError, AlertPersistenceError } from "../alerts" import { AlertComparator, AlertEventType, + AlertIncidentAckSource, + AlertIncidentEventType, AlertIncidentHoldReason, AlertIncidentStatus, AlertSeverity, @@ -13,7 +15,13 @@ import { AuthorizationV2 } from "./auth" import { wireExample, ListOf, ListQuery, Timestamp } from "./envelopes" import { V2ParameterInvalid } from "./errors" import { publicError } from "./public-error" -import { AlertIncidentPublicId, AlertRulePublicId, ErrorIssuePublicId } from "./resource-ids" +import { UserId } from "../../primitives" +import { + AlertIncidentEventPublicId, + AlertIncidentPublicId, + AlertRulePublicId, + ErrorIssuePublicId, +} from "./resource-ids" const alertIncidentExample = { id: "inc_tC4d9V79DCDzgbGKhAnff9", @@ -38,9 +46,22 @@ const alertIncidentExample = { last_notified_at: "2026-07-15T09:10:05.000Z", hold_reason: null, held_since: null, + acknowledged_at: "2026-07-15T09:14:02.000Z", + acknowledged_by: { user_id: "user_2Nk8mXqPfR3yZ1aB4cD5eF6g", name: "Dana Kim" }, + acknowledged_via: "slack", error_issue_id: null, } as const +const V2AlertIncidentActor = Schema.Struct({ + user_id: Schema.NullOr(UserId).annotate({ + description: "The Maple user, or `null` for a chat account not linked to one.", + }), + name: Schema.NullOr(Schema.String).annotate({ + description: "Display name as the source reported it, or `null` when none was available.", + examples: ["Dana Kim"], + }), +}).annotate({ identifier: "AlertIncidentActor", title: "Alert incident actor" }) + // v2 wire schemas are annotated `Schema.Struct`s (not `Schema.Class`) — see the // note in api-keys.ts. export const V2AlertIncident = Schema.Struct({ @@ -113,6 +134,18 @@ export const V2AlertIncident = Schema.Struct({ held_since: Schema.NullOr(Timestamp).annotate({ description: "When the current hold began, or `null` when the incident is not held.", }), + acknowledged_at: Schema.NullOr(Timestamp).annotate({ + description: + "When someone acknowledged this open incident, or `null`. While acknowledged, reminders (`renotify`) pause; an escalation to a higher severity still notifies and clears the acknowledgement. Cleared when the incident resolves.", + }), + acknowledged_by: Schema.NullOr(V2AlertIncidentActor).annotate({ + description: "Who acknowledged the incident, or `null` when it is not acknowledged.", + }), + acknowledged_via: Schema.NullOr(AlertIncidentAckSource).annotate({ + description: + "Where the acknowledgement came from (`web`, `api`, `mcp`, `slack`, `discord`), or `null` when not acknowledged.", + examples: ["slack"], + }), error_issue_id: Schema.NullOr(ErrorIssuePublicId).annotate({ description: "The linked error issue (`iss_…`) when the incident was correlated to one, or `null`.", }), @@ -120,11 +153,46 @@ export const V2AlertIncident = Schema.Struct({ identifier: "AlertIncident", title: "Alert Incident", description: - "A period during which an alert rule's condition held: opened after enough consecutive breaches, resolved by the scheduler after enough consecutive healthy checks. Read-only via the API.", + "A period during which an alert rule's condition held: opened after enough consecutive breaches, resolved by the scheduler after enough consecutive healthy checks. An open incident can be acknowledged to pause reminders; resolution is scheduler-driven.", examples: [wireExample(alertIncidentExample)], }) export type V2AlertIncident = Schema.Schema.Type +export const V2AlertIncidentEvent = Schema.Struct({ + id: AlertIncidentEventPublicId, + object: Schema.Literal("alert_incident_event").annotate({ + description: 'The object type — always `"alert_incident_event"`.', + examples: ["alert_incident_event"], + }), + incident_id: AlertIncidentPublicId, + type: AlertIncidentEventType.annotate({ + description: + "`acknowledged` or `unacknowledged` by a person, or `ack_cleared` when the scheduler cleared an acknowledgement because the incident escalated to a higher severity.", + examples: ["acknowledged"], + }), + actor: Schema.NullOr(V2AlertIncidentActor).annotate({ + description: "Who did it, or `null` for a scheduler-written event.", + }), + source: Schema.NullOr(AlertIncidentAckSource).annotate({ + description: "Where it came from, or `null` for a scheduler-written event.", + }), + created_at: Timestamp, +}).annotate({ + identifier: "AlertIncidentEvent", + title: "Alert Incident Event", + description: "One entry in an alert incident's activity log, oldest first.", +}) +export type V2AlertIncidentEvent = Schema.Schema.Type + +const AlertIncidentEventList = Schema.Struct({ + object: Schema.Literal("list"), + data: Schema.Array(V2AlertIncidentEvent), +}).annotate({ + identifier: "AlertIncidentEventList", + title: "Alert incident event list", + description: "An incident's activity, oldest first.", +}) + const IncidentsQuery = Schema.Struct({ ...ListQuery.fields, status: Schema.optional( @@ -138,6 +206,13 @@ const IncidentsQuery = Schema.Struct({ description: "Only return incidents opened by this alert rule (`alrt_…`).", }), ), + acknowledged: Schema.optional( + Schema.Literals(["true", "false"]).annotate({ + description: + "`true` for open incidents someone has acknowledged, `false` for open incidents nobody has. Implies `status=open`.", + examples: ["false"], + }), + ), }).annotate({ identifier: "AlertIncidentListQuery", title: "Alert incident list query", @@ -145,6 +220,8 @@ const IncidentsQuery = Schema.Struct({ }) const alertPersistence = publicError(AlertPersistenceError) +const alertIncidentNotFound = publicError(AlertIncidentNotFoundError) +const alertIncidentResolved = publicError(AlertIncidentResolvedError) const AlertIncidentList = ListOf(V2AlertIncident).annotate({ identifier: "AlertIncidentList", @@ -171,7 +248,7 @@ export class V2AlertIncidentsApiGroup extends HttpApiGroup.make("alertIncidents" HttpApiEndpoint.get("retrieve", "/:id", { params: { id: AlertIncidentPublicId }, success: V2AlertIncident, - error: [publicError(AlertIncidentNotFoundError), alertPersistence], + error: [alertIncidentNotFound, alertPersistence], }).annotateMerge( OpenApi.annotations({ identifier: "getAlertIncident", @@ -181,12 +258,54 @@ export class V2AlertIncidentsApiGroup extends HttpApiGroup.make("alertIncidents" }), ), ) + .add( + HttpApiEndpoint.post("acknowledge", "/:id/acknowledge", { + params: { id: AlertIncidentPublicId }, + success: V2AlertIncident, + error: [alertIncidentNotFound, alertIncidentResolved, alertPersistence], + }).annotateMerge( + OpenApi.annotations({ + identifier: "acknowledgeAlertIncident", + summary: "Acknowledge an alert incident", + description: + "Marks an open incident as acknowledged by the calling user, which pauses reminders until it is unacknowledged, escalates to a higher severity, or resolves. Acknowledging an already acknowledged incident changes nothing and returns it. Fails with 409 on a resolved incident. Requires the `alerts:write` scope.", + }), + ), + ) + .add( + HttpApiEndpoint.post("unacknowledge", "/:id/unacknowledge", { + params: { id: AlertIncidentPublicId }, + success: V2AlertIncident, + error: [alertIncidentNotFound, alertIncidentResolved, alertPersistence], + }).annotateMerge( + OpenApi.annotations({ + identifier: "unacknowledgeAlertIncident", + summary: "Unacknowledge an alert incident", + description: + "Clears an open incident's acknowledgement, so reminders resume on their usual schedule. Fails with 409 on a resolved incident. Requires the `alerts:write` scope.", + }), + ), + ) + .add( + HttpApiEndpoint.get("listEvents", "/:id/events", { + params: { id: AlertIncidentPublicId }, + success: AlertIncidentEventList, + error: [alertIncidentNotFound, alertPersistence], + }).annotateMerge( + OpenApi.annotations({ + identifier: "listAlertIncidentEvents", + summary: "List an alert incident's events", + description: + "Returns the incident's acknowledgement history, oldest first. Requires the `alerts:read` scope.", + }), + ), + ) .prefix("/v2/alerts/incidents") .middleware(AuthorizationV2) .annotateMerge( OpenApi.annotations({ title: "Alert Incidents", description: - "The incident history produced by your alert rules. Incidents open when a rule breaches for enough consecutive checks and resolve automatically once the signal recovers — this surface is read-only.", + "The incident history produced by your alert rules. Incidents open when a rule breaches for enough consecutive checks and resolve automatically once the signal recovers. An open incident can be acknowledged to pause its reminders.", }), ) {} diff --git a/packages/domain/src/http/v2/openapi.test.ts b/packages/domain/src/http/v2/openapi.test.ts index eec28a2b0d..ff2669f0ae 100644 --- a/packages/domain/src/http/v2/openapi.test.ts +++ b/packages/domain/src/http/v2/openapi.test.ts @@ -111,6 +111,7 @@ describe("MapleApiV2 OpenAPI", () => { "GET /v2/alerts/destinations/{id}", "GET /v2/alerts/incidents", "GET /v2/alerts/incidents/{id}", + "GET /v2/alerts/incidents/{id}/events", "GET /v2/alerts/rules", "GET /v2/alerts/rules/{id}", "GET /v2/alerts/rules/{id}/checks", @@ -213,6 +214,8 @@ describe("MapleApiV2 OpenAPI", () => { "POST /v2/alerts/destinations", "POST /v2/alerts/destinations/telegram/chats", "POST /v2/alerts/destinations/{id}/test", + "POST /v2/alerts/incidents/{id}/acknowledge", + "POST /v2/alerts/incidents/{id}/unacknowledge", "POST /v2/alerts/rules", "POST /v2/alerts/rules/preview", "POST /v2/alerts/rules/test", diff --git a/packages/domain/src/http/v2/resource-ids.ts b/packages/domain/src/http/v2/resource-ids.ts index f8b2afe7da..ef5aa99576 100644 --- a/packages/domain/src/http/v2/resource-ids.ts +++ b/packages/domain/src/http/v2/resource-ids.ts @@ -3,6 +3,7 @@ import { AgentFeedbackId, AlertDeliveryEventId, AlertDestinationId, + AlertIncidentEventId, AlertIncidentId, AlertRuleId, AnomalyIncidentId, @@ -20,6 +21,7 @@ export const AlertRulePublicId = PublicId(PublicIdPrefixes.alertRule, AlertRuleI export const AlertDestinationPublicId = PublicId(PublicIdPrefixes.alertDestination, AlertDestinationId) export const AlertIncidentPublicId = PublicId(PublicIdPrefixes.alertIncident, AlertIncidentId) export const AlertDeliveryEventPublicId = PublicId(PublicIdPrefixes.event, AlertDeliveryEventId) +export const AlertIncidentEventPublicId = PublicId(PublicIdPrefixes.event, AlertIncidentEventId) export const ActorPublicId = PublicId(PublicIdPrefixes.actor, ActorId) export const AnomalyIncidentPublicId = PublicId(PublicIdPrefixes.anomalyIncident, AnomalyIncidentId) export const ErrorIncidentPublicId = PublicId(PublicIdPrefixes.errorIncident, ErrorIncidentId) diff --git a/packages/domain/src/http/v2/v2-contract.test.ts b/packages/domain/src/http/v2/v2-contract.test.ts index 14c121983b..c7c4866226 100644 --- a/packages/domain/src/http/v2/v2-contract.test.ts +++ b/packages/domain/src/http/v2/v2-contract.test.ts @@ -478,6 +478,9 @@ describe("V2 alerts wire format", () => { last_notified_at: null, hold_reason: null, held_since: null, + acknowledged_at: null, + acknowledged_by: null, + acknowledged_via: null, error_issue_id: null, }) expect(incident.id).toBe(INCIDENT_UUID) diff --git a/packages/domain/src/mcp-outputs/alerts.ts b/packages/domain/src/mcp-outputs/alerts.ts index 438bc6ebe6..884043578e 100644 --- a/packages/domain/src/mcp-outputs/alerts.ts +++ b/packages/domain/src/mcp-outputs/alerts.ts @@ -216,6 +216,9 @@ export const AlertIncidentRow = Schema.Struct({ /** Set while an open incident is waiting on telemetry rather than observing a breach. */ holdReason: NullableString, heldSince: NullableString, + /** Set while an open incident is acknowledged; reminders pause until it is not. */ + acknowledgedAt: Schema.optionalKey(NullableString), + acknowledgedBy: Schema.optionalKey(NullableString), threshold: Schema.Number, comparator: Schema.String, firstTriggeredAt: Schema.String, @@ -298,6 +301,19 @@ export const IncidentTimelineRow = Schema.Struct({ lastTriggeredAt: Schema.String, resolvedAt: NullableString, lastNotifiedAt: NullableString, + acknowledgedAt: Schema.optionalKey(NullableString), + acknowledgedBy: Schema.optionalKey(NullableString), +}) + +export const AcknowledgeAlertIncidentOutput = Schema.Struct({ + id: Schema.String, + ruleName: Schema.String, + status: Schema.String, + acknowledged: Schema.Boolean, + acknowledgedAt: NullableString, + acknowledgedBy: NullableString, + /** False when the incident was already in the requested state. */ + changed: Schema.Boolean, }) export const GetIncidentTimelineOutput = Schema.Struct({ diff --git a/packages/domain/src/mcp-outputs/catalog.ts b/packages/domain/src/mcp-outputs/catalog.ts index 0d9d871079..e279057cef 100644 --- a/packages/domain/src/mcp-outputs/catalog.ts +++ b/packages/domain/src/mcp-outputs/catalog.ts @@ -34,6 +34,7 @@ import { } from "./integrations" import { CiRunsOutput, CompareCommitsOutput } from "./ci" import { + AcknowledgeAlertIncidentOutput, CreateAlertRuleOutput, DeleteAlertRuleOutput, GetAlertRuleOutput, @@ -148,6 +149,7 @@ export const McpToolOutputs = { delete_ingest_rule: DeleteIngestRuleOutput, delete_scrape_target: DeleteScrapeTargetOutput, delete_alert_rule: DeleteAlertRuleOutput, + acknowledge_alert_incident: AcknowledgeAlertIncidentOutput, delete_uptime_monitor: DeleteUptimeMonitorOutput, describe_dashboard_schema: DescribeDashboardSchemaOutput, describe_warehouse_tables: DescribeWarehouseTablesOutput, diff --git a/packages/primitives/src/index.ts b/packages/primitives/src/index.ts index e03ddb1d5a..d066156105 100644 --- a/packages/primitives/src/index.ts +++ b/packages/primitives/src/index.ts @@ -196,6 +196,9 @@ export type AlertIncidentId = Schema.Schema.Type export const AlertDeliveryEventId = MapleUuidId("@maple/AlertDeliveryEventId", "Alert Delivery Event ID") export type AlertDeliveryEventId = Schema.Schema.Type +export const AlertIncidentEventId = MapleUuidId("@maple/AlertIncidentEventId", "Alert Incident Event ID") +export type AlertIncidentEventId = Schema.Schema.Type + export const MobileDeviceId = MapleUuidId("@maple/MobileDeviceId", "Mobile Device ID") export type MobileDeviceId = Schema.Schema.Type diff --git a/packages/web-data/src/alerts/headroom.test.ts b/packages/web-data/src/alerts/headroom.test.ts index 4ce6530efb..8840efcf9e 100644 --- a/packages/web-data/src/alerts/headroom.test.ts +++ b/packages/web-data/src/alerts/headroom.test.ts @@ -129,6 +129,10 @@ describe("quietForMs", () => { lastNotifiedAt: null, holdReason: null, heldSince: null, + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, errorIssueId: null, }) diff --git a/packages/web-data/src/alerts/incident-timeline.test.ts b/packages/web-data/src/alerts/incident-timeline.test.ts index a8f47bbf17..4ae08e547f 100644 --- a/packages/web-data/src/alerts/incident-timeline.test.ts +++ b/packages/web-data/src/alerts/incident-timeline.test.ts @@ -32,6 +32,10 @@ function makeIncident(overrides: Record = {}): AlertIncidentDoc lastNotifiedAt: "2026-10-15T14:14:05.000Z", holdReason: null, heldSince: null, + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, errorIssueId: null, ...overrides, }) diff --git a/packages/web-data/src/alerts/overview-derive.test.ts b/packages/web-data/src/alerts/overview-derive.test.ts index 21307a8acc..d86f9b1334 100644 --- a/packages/web-data/src/alerts/overview-derive.test.ts +++ b/packages/web-data/src/alerts/overview-derive.test.ts @@ -96,6 +96,10 @@ function makeIncident(overrides: Record = {}): AlertIncidentDoc lastNotifiedAt: null, holdReason: null, heldSince: null, + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, errorIssueId: null, ...overrides, }) diff --git a/packages/web-data/src/alerts/overview.test.ts b/packages/web-data/src/alerts/overview.test.ts index a06be184cd..b6c186c0ed 100644 --- a/packages/web-data/src/alerts/overview.test.ts +++ b/packages/web-data/src/alerts/overview.test.ts @@ -84,6 +84,10 @@ function makeIncident(overrides: Record = {}): AlertIncidentDoc lastNotifiedAt: null, holdReason: null, heldSince: null, + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, errorIssueId: null, ...overrides, }) diff --git a/packages/web-data/src/alerts/preview-insights.test.ts b/packages/web-data/src/alerts/preview-insights.test.ts index cd3d04ed3e..654275dd64 100644 --- a/packages/web-data/src/alerts/preview-insights.test.ts +++ b/packages/web-data/src/alerts/preview-insights.test.ts @@ -75,6 +75,10 @@ function incident(groupKey: string | null, status = "open"): AlertIncidentDocume lastNotifiedAt: at(0), holdReason: null, heldSince: null, + acknowledgedAt: null, + acknowledgedByUserId: null, + acknowledgedByName: null, + acknowledgedVia: null, errorIssueId: null, }) } diff --git a/packages/web-data/src/collections/alerts.ts b/packages/web-data/src/collections/alerts.ts index b8f01102ec..68314ed8e7 100644 --- a/packages/web-data/src/collections/alerts.ts +++ b/packages/web-data/src/collections/alerts.ts @@ -8,6 +8,7 @@ import { AlertEventType, AlertGroupBy, AlertIncidentDocument, + AlertIncidentAckSource, AlertIncidentHoldReason, AlertIncidentStatus, AlertNotificationTemplate, @@ -45,6 +46,9 @@ const asEvaluationInterval = Schema.decodeUnknownSync(Schema.NullOr(AlertEvaluat const decodeErrorKind = Schema.decodeUnknownOption(AlertEvaluationErrorKind) const asIncidentStatus = Schema.decodeUnknownSync(AlertIncidentStatus) const asHoldReason = Schema.decodeUnknownSync(AlertIncidentHoldReason) +// Lenient: a source added by a newer server reads as unknown rather than breaking the alerts list. +const decodeAckSource = Schema.decodeUnknownOption(AlertIncidentAckSource) +const decodeUserId = Schema.decodeUnknownOption(UserId) const asEventType = Schema.decodeUnknownSync(AlertEventType) const asReducer = Schema.decodeUnknownSync(QueryEngineAlertReducer) // Lenient: a behavior added by a newer server reads as skip rather than breaking the alerts list. @@ -164,6 +168,10 @@ export const AlertIncidentRowSchema = Schema.Struct({ last_notified_at: Schema.NullOr(Schema.String), hold_reason: Schema.NullOr(Schema.String), held_since: Schema.NullOr(Schema.String), + acknowledged_at: Schema.optionalKey(Schema.NullOr(Schema.String)), + acknowledged_by_user_id: Schema.optionalKey(Schema.NullOr(Schema.String)), + acknowledged_by_name: Schema.optionalKey(Schema.NullOr(Schema.String)), + acknowledged_via: Schema.optionalKey(Schema.NullOr(Schema.String)), error_issue_id: Schema.NullOr(Schema.String), created_at: Schema.String, updated_at: Schema.String, @@ -293,6 +301,14 @@ export const rowToAlertIncidentDocument = (row: AlertIncidentRow): AlertIncident lastNotifiedAt: row.last_notified_at != null ? decodeIso(row.last_notified_at) : null, holdReason: row.hold_reason != null ? asHoldReason(row.hold_reason) : null, heldSince: row.held_since != null ? decodeIso(row.held_since) : null, + acknowledgedAt: row.acknowledged_at != null ? decodeIso(row.acknowledged_at) : null, + acknowledgedByUserId: + row.acknowledged_by_user_id != null + ? Option.getOrNull(decodeUserId(row.acknowledged_by_user_id)) + : null, + acknowledgedByName: row.acknowledged_by_name ?? null, + acknowledgedVia: + row.acknowledged_via != null ? Option.getOrNull(decodeAckSource(row.acknowledged_via)) : null, errorIssueId: row.error_issue_id != null ? asErrorIssueId(row.error_issue_id) : null, }) From 5a66d2bda5e1d271d2adb90f80a2a5b85216f449 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 01:27:39 +0100 Subject: [PATCH 02/29] fix(alerts): clear an ack on escalation in the claimed write, decided in SQL The escalation cleared the ack from the tick's prefetch and wrote ack_cleared after the claimed update, outside it. A failed event insert could then lose the escalation page, and a human ack or unack during the evaluation got the wrong outcome. Every escalation now clears whatever ack stands when it commits, with a guarded update in the same claimed write, and writes ack_cleared only when one was cleared. The planner no longer carries the flag. Also: an API-key ack records no user (the key is not a person), the unacknowledge docs say an overdue reminder goes out on the next check, and the MCP incident list takes an acknowledged filter. --- apps/ai/src/mcp/tool-descriptors.generated.ts | 6 +- .../mcp/tools/acknowledge-alert-incident.ts | 9 +- apps/ai/src/mcp/tools/list-alert-incidents.ts | 11 ++ .../api/src/routes/v2/alert-incidents.http.ts | 6 +- apps/api/src/routes/v2/alerts.http.test.ts | 6 +- packages/alerting-core/src/index.ts | 11 +- packages/alerting-core/src/tiers.test.ts | 6 +- .../src/services/alerts/AlertsService.test.ts | 82 ++++++++++++++ .../src/services/alerts/AlertsService.ts | 102 ++++++++++-------- .../domain/src/http/v2/alert-incidents.ts | 4 +- packages/domain/src/mcp-outputs/alerts.ts | 2 + 11 files changed, 174 insertions(+), 71 deletions(-) diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 11e322ff5d..2a5b595092 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -30,14 +30,14 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"list_alert_rules","title":"List Alert Rules","description":"List configured alert rules with severity, signal type and condition. Use list_alert_incidents for what has fired.","inputSchema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"string"},"description":"Only rules covering any of these services: scoped to them, scoped to every service, or raw_query rules whose SQL or name mentions them"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"Only rules on this signal type"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only rules with this severity"},"enabled_only":{"type":"boolean","description":"Only enabled rules"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"services":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"severity":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["rules","total"],"additionalProperties":true},"annotations":{"title":"List Alert Rules","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_destinations","title":"List Alert Destinations","description":"List the notification destinations a rule can deliver to, with id, type and delivery health. Pass the ids as destination_ids to create_alert_rule / update_alert_rule.","inputSchema":{"type":"object","properties":{"type":{"type":"string","enum":["pagerduty","webhook","discord","telegram","google-chat","email","chat","linear"],"description":"Only destinations of this type"},"enabled_only":{"type":"boolean","description":"Only enabled destinations"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"enabled":{"type":"boolean"},"summary":{"type":"string"},"channelLabel":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestError":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabledReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","type","enabled","summary","channelLabel","lastTestedAt","lastTestError","disabledReason","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"type":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["destinations","total"],"additionalProperties":true},"annotations":{"title":"List Alert Destinations","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_alert_rule","title":"Get Alert Rule","description":"Get full configuration details of a specific alert rule including thresholds, service filters, evaluation settings, and notification destinations. Use list_alert_rules to find rule IDs.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"excludeServiceNames":{"type":"array","items":{"type":"string"}},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noDataBehavior":{"type":"string"},"consecutiveBreachesRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthyRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"renotifyIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"queryBuilderDraft":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"null"}]},"rawQuerySql":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawQueryReducer":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"notificationTitle":{"anyOf":[{"type":"string"},{"type":"null"}]},"notificationBody":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationError":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationErrorKind":{"anyOf":[{"type":"string"},{"type":"null"}]},"slowedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}},"required":["id","name","type","enabled"],"additionalProperties":true}}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt","excludeServiceNames","groupBy","minimumSampleCount","noDataBehavior","consecutiveBreachesRequired","consecutiveHealthyRequired","renotifyIntervalMinutes","apdexThresholdMs","queryBuilderDraft","rawQuerySql","rawQueryReducer"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Get Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"list_alert_incidents","title":"List Alert Incidents","description":"Alert incidents: alert-rule breaches, open and resolved, with severity, group, condition and last observed value (flare-ups of an error issue are list_error_incidents). For one rule's trigger, notify and resolve timestamps use get_incident_timeline.","inputSchema":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 50, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"holdReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"heldSince":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"firstTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","holdReason","heldSince","threshold","comparator","firstTriggeredAt","resolvedAt","lastObservedValue"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"recentlyResolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"List Alert Incidents","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"list_alert_incidents","title":"List Alert Incidents","description":"Alert incidents: alert-rule breaches, open and resolved, with severity, group, condition and last observed value (flare-ups of an error issue are list_error_incidents). For one rule's trigger, notify and resolve timestamps use get_incident_timeline.","inputSchema":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"acknowledged":{"type":"boolean","description":"true: open incidents someone acknowledged; false: open incidents nobody has. Implies status=open"},"limit":{"type":"number","description":"Max incidents to return (default 50, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"holdReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"heldSince":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"firstTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","holdReason","heldSince","threshold","comparator","firstTriggeredAt","resolvedAt","lastObservedValue"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"recentlyResolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"List Alert Incidents","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_checks","title":"List Alert Checks","description":"List a rule's recent checks (one row per evaluation) with observed value, threshold, sample count and incident transition. Use it to tune thresholds, diagnose flapping, or see the near-misses before a breach.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"group_key":{"type":"string","description":"Only checks for this exact group key"},"status":{"type":"string","enum":["breached","healthy","skipped","error"],"description":"Only checks with this status"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"limit":{"type":"number","description":"Max checks to return (default 100, max 2000)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"skipped":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errored":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transitions":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"checks":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"string"},"groupKey":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"observedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowStart":{"type":"string"},"windowEnd":{"type":"string"},"consecutiveBreaches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"incidentId":{"anyOf":[{"type":"string"},{"type":"null"}]},"incidentTransition":{"type":"string"},"evaluationDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errorMessage":{"anyOf":[{"type":"string"},{"type":"null"}]},"errorCategory":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["timestamp","groupKey","status","skipReason","observedValue","threshold","comparator","sampleCount","windowStart","windowEnd","consecutiveBreaches","consecutiveHealthy","incidentId","incidentTransition","evaluationDurationMs","errorMessage","errorCategory"],"additionalProperties":true}},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"groupKey":{"type":"string"},"status":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["ruleId","total","breached","healthy","skipped","noData","belowMinSamples","errored","transitions","checks"],"additionalProperties":true},"annotations":{"title":"List Alert Checks","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations).","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_alert_rule","title":"Delete Alert Rule","description":"Permanently delete an alert rule together with its incident history, delivery events and evaluation state. Irreversible; requires confirm=true. Ids from list_alert_rules.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"confirm":{"type":"boolean","description":"Must be true. There is no undo."}},"required":["rule_id","confirm"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"annotations":{"title":"Delete Alert Rule","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, - {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleId","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_dashboard","title":"Get Dashboard","description":"Full configuration of one dashboard: every widget with its id, visualization, dataSource, display, layout and any pinned timeRange. Widgets come back in the shape update_dashboard_widget and replace_dashboard_widgets take. Ids from list_dashboards.","inputSchema":{"type":"object","properties":{"dashboard_id":{"type":"string","description":"Dashboard ID: its UUID or its REST id (dash_\u2026)"}},"required":["dashboard_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"publicId":{"type":"string"},"dashboard":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"widgets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"visualization":{"type":"string","enum":["chart","hbar","stat","gauge","table","list","pie","histogram","heatmap","funnel","paths","markdown"]},"dataSource":{"$ref":"#/$defs/@maple~1WidgetDataSource"},"display":{"type":"object","properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartPresentation":{"anyOf":[{"type":"object","properties":{"legend":{"anyOf":[{"type":"string","enum":["visible","hidden","right"]},{"type":"null"}]},"seriesStats":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tooltip":{"anyOf":[{"type":"string","enum":["visible","hidden"]},{"type":"null"}]},"showPoints":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fillNulls":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"boolean","enum":[false]},{"type":"null"}]},"compareToPreviousPeriod":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"xAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"yAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMin":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMax":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScale":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fitYAxisToData":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"seriesMapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"colorOverrides":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"stacked":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"curveType":{"anyOf":[{"type":"string","enum":["linear","monotone"]},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}]},"sparkline":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"dataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1WidgetDataSource"},{"type":"null"}]}},"required":["enabled"],"additionalProperties":true},{"type":"null"}]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"header":{"type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"align":{"anyOf":[{"type":"string","enum":["left","center","right"]},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]}},"required":["field","header"],"additionalProperties":true}},{"type":"null"}]},"listDataSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"listWhereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"listLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"listRootOnly":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"pie":{"anyOf":[{"type":"object","properties":{"donut":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"innerRadius":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"showLabels":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"showPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"funnel":{"anyOf":[{"type":"object","properties":{"showStepPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["session"]},"dimension":{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},"value":{"type":"string"}},"required":["kind","dimension","value"],"additionalProperties":true}]}},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"breakdownBy":{"anyOf":[{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},{"type":"string","pattern":"^attribute:[\\s\\S]*?$"},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","enum":["bars","dropoff"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"paths":{"anyOf":[{"type":"object","properties":{"anchor":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true}]},"direction":{"anyOf":[{"type":"string","enum":["after","before"]},{"type":"null"}]},"depth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"branches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"include":{"anyOf":[{"type":"string","enum":["all","events","pages"]},{"type":"null"}]},"exclude":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]}},"required":["anchor"],"additionalProperties":true},{"type":"null"}]},"histogram":{"anyOf":[{"type":"object","properties":{"bucketCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"bucketWidth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScaleY":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"heatmap":{"anyOf":[{"type":"object","properties":{"colorScale":{"anyOf":[{"type":"string","enum":["amber","blues","reds","viridis","magma","cividis"]},{"type":"null"}]},"scaleType":{"anyOf":[{"type":"string","enum":["linear","log"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"gauge":{"anyOf":[{"type":"object","properties":{"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"style":{"anyOf":[{"type":"string","enum":["radial","bar"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"markdown":{"anyOf":[{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"layout":{"type":"object","properties":{"x":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"y":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"w":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"h":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["x","y","w","h"],"additionalProperties":true},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"sectionId":{"type":"string"},"tabId":{"type":"string"}},"required":["id","visualization","dataSource","display","layout"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","tags","timeRange","widgets","createdAt","updatedAt"],"additionalProperties":true}},"required":["publicId","dashboard"],"additionalProperties":true,"$defs":{"@maple/IsoDateTimeString":{"type":"string"},"@maple/QueryWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["query"]},"resultShape":{"type":"string","enum":["timeseries","breakdown","list"]},"queries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true}]}},"formulas":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expression":{"type":"string"},"legend":{"type":"string"},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","expression","legend"],"additionalProperties":true}},{"type":"null"}]},"comparison":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"type":"string","enum":["none","previous_period"]},{"type":"null"}]},"includePercentChange":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"defaultLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","resultShape","queries"],"additionalProperties":true,"title":"Query widget data source"},"@maple/RawSqlWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["raw_sql"]},"sql":{"type":"string"},"displayType":{"type":"string"},"granularitySeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","sql"],"additionalProperties":true,"title":"Raw SQL widget data source"},"@maple/RouteWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["route"]},"endpoint":{"type":"string"},"params":{"type":"object"},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","endpoint"],"additionalProperties":true,"title":"Route widget data source"},"@maple/StaticWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["static"]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind"],"additionalProperties":true,"title":"Static widget data source"},"@maple/WidgetDataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1QueryWidgetDataSource"},{"$ref":"#/$defs/@maple~1RawSqlWidgetDataSource"},{"$ref":"#/$defs/@maple~1RouteWidgetDataSource"},{"$ref":"#/$defs/@maple~1StaticWidgetDataSource"}],"title":"Widget data source"}}},"annotations":{"title":"Get Dashboard","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts index 3491456709..8b4a07bb3a 100644 --- a/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts +++ b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts @@ -60,6 +60,7 @@ export function registerAcknowledgeAlertIncidentTool(server: McpToolRegistrar) { const { incident } = result return { id: incident.id, + ruleId: incident.ruleId, ruleName: incident.ruleName, status: incident.status, acknowledged: incident.acknowledgedAt !== null, @@ -86,7 +87,13 @@ export function registerAcknowledgeAlertIncidentTool(server: McpToolRegistrar) { ["Acknowledged at", output.acknowledgedAt ?? undefined], ]), ], - next: [doc.next("get_incident_timeline", {}, "the incident timeline")], + next: [ + doc.next( + "get_incident_timeline", + { rule_id: output.ruleId }, + "this rule's incident timeline", + ), + ], }), }) } diff --git a/apps/ai/src/mcp/tools/list-alert-incidents.ts b/apps/ai/src/mcp/tools/list-alert-incidents.ts index 50cba7c9b0..f2b1b675c3 100644 --- a/apps/ai/src/mcp/tools/list-alert-incidents.ts +++ b/apps/ai/src/mcp/tools/list-alert-incidents.ts @@ -25,6 +25,9 @@ export function registerListAlertIncidentsTool(server: McpToolRegistrar) { status: P.optionalOneOf(ALERT_INCIDENT_STATUSES, "Only incidents in this status"), severity: P.optionalOneOf(ALERT_SEVERITIES, "Only incidents with this severity"), group_key: P.optionalText("Only incidents with this exact group key"), + acknowledged: P.optionalFlag( + "true: open incidents someone acknowledged; false: open incidents nobody has. Implies status=open", + ), limit: P.limit({ default: 50, max: MAX_LIMIT, noun: "incidents" }), }), output: ListAlertIncidentsOutput, @@ -38,6 +41,9 @@ export function registerListAlertIncidentsTool(server: McpToolRegistrar) { const result = yield* readModels .listIncidents(tenant.orgId, { ...(params.status === undefined ? undefined : { status: params.status }), + ...(params.acknowledged === undefined + ? undefined + : { acknowledged: params.acknowledged }), limit: inMemoryFilter ? FILTERED_SCAN : params.limit + 1, }) .pipe( @@ -120,6 +126,7 @@ export function registerListAlertIncidentsTool(server: McpToolRegistrar) { ...(params.status === undefined ? undefined : { status: params.status }), ...(params.severity === undefined ? undefined : { severity: params.severity }), ...(params.group_key === undefined ? undefined : { groupKey: params.group_key }), + ...(params.acknowledged === undefined ? undefined : { acknowledged: params.acknowledged }), limit: params.limit, hasMore: matching.length > params.limit, } @@ -158,6 +165,10 @@ export function registerListAlertIncidentsTool(server: McpToolRegistrar) { ["Status", output.status], ["Severity", output.severity], ["Group", output.groupKey], + [ + "Acknowledged", + output.acknowledged === undefined ? undefined : output.acknowledged ? "yes" : "no", + ], ], ...(scanNotice === undefined ? undefined : { notices: [scanNotice] }), ...(output.incidents.length === 0 diff --git a/apps/api/src/routes/v2/alert-incidents.http.ts b/apps/api/src/routes/v2/alert-incidents.http.ts index 6ba0a28a02..9235babcda 100644 --- a/apps/api/src/routes/v2/alert-incidents.http.ts +++ b/apps/api/src/routes/v2/alert-incidents.http.ts @@ -64,8 +64,8 @@ export const HttpV2AlertIncidentsLive = HttpApiBuilder.group(MapleApiV2, "alertI const members = yield* OrgMembersService // Who is acknowledging, with a name recorded alongside so every surface can show it without a - // directory lookup. An API key is named by its label; a session by the member directory, best - // effort: a slow or unconfigured directory records the ack without a name. + // directory lookup. An API key is not a person: it is named by its label and has no user. A + // session is named by the member directory, best effort: a slow one records no name. const callerActor = Effect.gen(function* () { const tenant = yield* CurrentTenant.Context const credential = yield* CurrentAuditActor @@ -81,7 +81,7 @@ export const HttpV2AlertIncidentsLive = HttpApiBuilder.group(MapleApiV2, "alertI ), ) const actor: AlertIncidentActor = { - userId: tenant.userId, + userId: credential?.type === "api_key" ? null : tenant.userId, name, source: credential?.source === "dashboard" ? "web" : "api", } diff --git a/apps/api/src/routes/v2/alerts.http.test.ts b/apps/api/src/routes/v2/alerts.http.test.ts index facfbb506f..9db81c6bf2 100644 --- a/apps/api/src/routes/v2/alerts.http.test.ts +++ b/apps/api/src/routes/v2/alerts.http.test.ts @@ -1180,8 +1180,8 @@ describe("v2 alerts over HTTP", () => { expect(acked.status).toBe(200) expect(acked.body.status).toBe("open") expect(acked.body.acknowledged_at).toEqual(expect.any(String)) - // An API key is named by its label. - expect(acked.body.acknowledged_by).toEqual({ user_id: "user_alerts_e2e", name: "alerts-test" }) + // An API key is not a person: it is named by its label and has no user. + expect(acked.body.acknowledged_by).toEqual({ user_id: null, name: "alerts-test" }) expect(acked.body.acknowledged_via).toBe("api") const unacknowledgedOnly = await harness.request( @@ -1225,7 +1225,7 @@ describe("v2 alerts over HTTP", () => { expect(events.body.data[0]).toMatchObject({ object: "alert_incident_event", incident_id: openId, - actor: { user_id: "user_alerts_e2e", name: "alerts-test" }, + actor: { user_id: null, name: "alerts-test" }, source: "api", }) expect(events.body.data[0].id).toMatch(/^evt_/) diff --git a/packages/alerting-core/src/index.ts b/packages/alerting-core/src/index.ts index 8c4620679a..1cf542e41f 100644 --- a/packages/alerting-core/src/index.ts +++ b/packages/alerting-core/src/index.ts @@ -308,8 +308,6 @@ export interface AlertLifecyclePlan { readonly inheritedNotificationAtMs: number | null /** Whether the host must advance lastNotifiedAt before queueing the event. */ readonly advanceNotificationAnchor: boolean - /** Whether the host must clear the incident's acknowledgement (an escalation pages again). */ - readonly clearAcknowledgement: boolean } export interface AlertDeliveryRetryPolicy { @@ -362,7 +360,6 @@ const noTransition = ( hold, inheritedNotificationAtMs: null, advanceNotificationAnchor: false, - clearAcknowledgement: false, }) const resolutionPlan = ( @@ -380,7 +377,6 @@ const resolutionPlan = ( hold: null, inheritedNotificationAtMs: null, advanceNotificationAnchor: false, - clearAcknowledgement: false, } } @@ -445,7 +441,6 @@ export const planAlertLifecycle = (input: AlertLifecycleInput): Effect.Effect { transition: "continued", eventType: null, advanceNotificationAnchor: false, - clearAcknowledgement: false, }) }) - it("pages an escalation despite the ack, and asks the host to clear it", () => { + it("pages an escalation despite the ack", () => { expect(tick(25, openAt("warning", true))).toMatchObject({ transition: "escalated", eventType: "trigger", advanceNotificationAnchor: true, - clearAcknowledgement: true, }) - expect(tick(25, openAt("warning", false))).toMatchObject({ clearAcknowledgement: false }) }) it("de-escalates quietly while acknowledged", () => { @@ -344,7 +341,6 @@ describe("acknowledged incidents", () => { severity: "warning", eventType: null, advanceNotificationAnchor: false, - clearAcknowledgement: false, }) }) diff --git a/packages/backend/src/services/alerts/AlertsService.test.ts b/packages/backend/src/services/alerts/AlertsService.test.ts index 0082f6aa93..9a87d804c5 100644 --- a/packages/backend/src/services/alerts/AlertsService.test.ts +++ b/packages/backend/src/services/alerts/AlertsService.test.ts @@ -1056,6 +1056,88 @@ describe("AlertsService", () => { ) }) + it.effect( + "an acknowledged incident de-escalates quietly and escalates loudly, clearing the ack once", + () => { + const testDb = createTestDb(trackedDbs) + const state = { + tracesAggregateRows: errorRateRows(30) as ReadonlyArray>, + } + const requests: Array = [] + const tick = (alerts: AlertsServiceApi) => + Effect.gen(function* () { + yield* TestClock.adjust(Duration.minutes(1)) + yield* alerts.runSchedulerTick() + }) + const acknowledge = (orgId: OrgId) => + Effect.promise(() => + executeSql( + testDb, + `update alert_incidents set acknowledged_at = now(), acknowledged_by_name = 'Dana', + acknowledged_via = 'web' where org_id = $1`, + [orgId], + ), + ) + const ackEvents = (orgId: OrgId) => + Effect.promise(() => + testDb.pglite.query<{ type: string }>( + "select type from alert_incident_events where org_id = $1 order by created_at", + [orgId], + ), + ).pipe(Effect.map((result) => Arr.map(result.rows, (row) => row.type))) + + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const alerts = yield* AlertsService + const orgId = asOrgId("org_tiers_ack") + yield* createTieredSetup(alerts, orgId, asUserId("user_tiers_ack")) + + yield* alerts.runSchedulerTick() + yield* tick(alerts) + const opened = (yield* alerts.listIncidents(orgId)).incidents[0] + assert.strictEqual(opened?.severity, "critical") + yield* acknowledge(orgId) + + // Down to warning while acknowledged: no notice, the reminder clock stays where it was. + state.tracesAggregateRows = errorRateRows(10) + yield* tick(alerts) + yield* tick(alerts) + const quiet = (yield* alerts.listIncidents(orgId)).incidents[0] + assert.strictEqual(quiet?.severity, "warning") + assert.strictEqual(quiet?.lastNotifiedAt, opened?.lastNotifiedAt) + assert.isNotNull(quiet?.acknowledgedAt) + assert.lengthOf(requests, 2) + + // Back to critical: it pages despite the ack, and the ack is cleared with one event. + state.tracesAggregateRows = errorRateRows(30) + yield* tick(alerts) + yield* tick(alerts) + const escalated = (yield* alerts.listIncidents(orgId)).incidents[0] + assert.strictEqual(escalated?.severity, "critical") + assert.isNull(escalated?.acknowledgedAt) + assert.isNull(escalated?.acknowledgedByName) + assert.sameMembers(requests.slice(2), [ + "trigger https://example.com/warn", + "trigger https://example.com/page", + ]) + assert.deepStrictEqual(yield* ackEvents(orgId), ["ack_cleared"]) + + // An escalation with no ack standing writes no event. + state.tracesAggregateRows = errorRateRows(10) + yield* tick(alerts) + yield* tick(alerts) + state.tracesAggregateRows = errorRateRows(30) + yield* tick(alerts) + yield* tick(alerts) + assert.deepStrictEqual(yield* ackEvents(orgId), ["ack_cleared"]) + }).pipe( + Effect.provide( + makeLayer(testDb, makeWarehouseStub(state), { fetch: recordingFetch(requests) }), + ), + ) + }, + ) + it.effect("resolves a warning incident ended by a rule edit without paging", () => { const testDb = createTestDb(trackedDbs) const state = { tracesAggregateRows: errorRateRows(10) as ReadonlyArray> } diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 175e3506d4..81c30b2a8b 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -3279,53 +3279,65 @@ export class AlertsService extends Context.Service - db.run( - PG.update(AlertIncidents) - .set({ - // A rule edit can remap the severity without a transition. - ...(severity !== openIncident.severity - ? { - severity, - peakSeverity, - threshold: tierRule.threshold, - thresholdUpper: tierRule.thresholdUpper, - } - : undefined), - lastTriggeredAt: timestamp, - lastObservedValue: evaluation.value, - lastSampleCount: evaluation.sampleCount, - lastEvaluatedAt: timestamp, - holdReason: null, - heldSince: null, - updatedAt: timestamp, - lastNotifiedAt: lifecycle.advanceNotificationAnchor - ? timestamp - : openIncident.lastNotifiedAt, - ...(lifecycle.clearAcknowledgement - ? clearedAcknowledgement - : undefined), - }) - .where(($) => [$.id.eq(openIncident.id), $.status.eq("open")]) - .returning("id"), - ), + Effect.gen(function* () { + const updated = yield* db.run( + PG.update(AlertIncidents) + .set({ + // A rule edit can remap the severity without a transition. + ...(severity !== openIncident.severity + ? { + severity, + peakSeverity, + threshold: tierRule.threshold, + thresholdUpper: tierRule.thresholdUpper, + } + : undefined), + lastTriggeredAt: timestamp, + lastObservedValue: evaluation.value, + lastSampleCount: evaluation.sampleCount, + lastEvaluatedAt: timestamp, + holdReason: null, + heldSince: null, + updatedAt: timestamp, + lastNotifiedAt: lifecycle.advanceNotificationAnchor + ? timestamp + : openIncident.lastNotifiedAt, + }) + .where(($) => [$.id.eq(openIncident.id), $.status.eq("open")]) + .returning("id"), + ) + if (updated.length === 0 || lifecycle.transition !== "escalated") + return updated + const cleared = yield* db.run( + PG.update(AlertIncidents) + .set(clearedAcknowledgement) + .where(($) => [ + $.id.eq(openIncident.id), + $.status.eq("open"), + $.acknowledgedAt.isNotNull(), + ]) + .returning("id"), + ) + if (cleared.length > 0) { + yield* db.run( + PG.insertInto(AlertIncidentEvents).values({ + id: decodeAlertIncidentEventIdSync(makeUuid()), + orgId: row.orgId, + incidentId: openIncident.id, + type: "ack_cleared", + actorUserId: null, + actorName: null, + source: null, + createdAt: timestamp, + }), + ) + } + return updated + }), ) - if (refreshed.length > 0 && lifecycle.clearAcknowledgement) { - yield* dbExecute((db) => - db.run( - PG.insertInto(AlertIncidentEvents).values({ - id: decodeAlertIncidentEventIdSync(makeUuid()), - orgId: row.orgId, - incidentId: openIncident.id, - type: "ack_cleared", - actorUserId: null, - actorName: null, - source: null, - createdAt: timestamp, - }), - ), - ) - } if (refreshed.length === 0) { return { transition: "none" as const, diff --git a/packages/domain/src/http/v2/alert-incidents.ts b/packages/domain/src/http/v2/alert-incidents.ts index 5dd1e7e177..6b73520577 100644 --- a/packages/domain/src/http/v2/alert-incidents.ts +++ b/packages/domain/src/http/v2/alert-incidents.ts @@ -282,7 +282,7 @@ export class V2AlertIncidentsApiGroup extends HttpApiGroup.make("alertIncidents" identifier: "unacknowledgeAlertIncident", summary: "Unacknowledge an alert incident", description: - "Clears an open incident's acknowledgement, so reminders resume on their usual schedule. Fails with 409 on a resolved incident. Requires the `alerts:write` scope.", + "Clears an open incident's acknowledgement, so reminders resume: one that came due while it was acknowledged is sent on the next check. Fails with 409 on a resolved incident. Requires the `alerts:write` scope.", }), ), ) @@ -296,7 +296,7 @@ export class V2AlertIncidentsApiGroup extends HttpApiGroup.make("alertIncidents" identifier: "listAlertIncidentEvents", summary: "List an alert incident's events", description: - "Returns the incident's acknowledgement history, oldest first. Requires the `alerts:read` scope.", + "Returns the incident's acknowledgement history, oldest first, up to its 500 most recent entries. Requires the `alerts:read` scope.", }), ), ) diff --git a/packages/domain/src/mcp-outputs/alerts.ts b/packages/domain/src/mcp-outputs/alerts.ts index 884043578e..17e5776ed7 100644 --- a/packages/domain/src/mcp-outputs/alerts.ts +++ b/packages/domain/src/mcp-outputs/alerts.ts @@ -231,6 +231,7 @@ const IncidentFilters = { status: Schema.optionalKey(Schema.String), severity: Schema.optionalKey(Schema.String), groupKey: Schema.optionalKey(Schema.String), + acknowledged: Schema.optionalKey(Schema.Boolean), limit: Schema.optionalKey(Schema.Number), /** More incidents matched than `limit` let through. */ hasMore: Schema.optionalKey(Schema.Boolean), @@ -307,6 +308,7 @@ export const IncidentTimelineRow = Schema.Struct({ export const AcknowledgeAlertIncidentOutput = Schema.Struct({ id: Schema.String, + ruleId: Schema.String, ruleName: Schema.String, status: Schema.String, acknowledged: Schema.Boolean, From 836f27a97aa899c5371c79fb01fca415a6cd706c Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 01:37:36 +0100 Subject: [PATCH 03/29] chore(ios): regenerate the OpenAPI spec for the incident acknowledgement fields --- .../MapleAPI/Sources/MapleAPI/openapi.json | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json b/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json index e7cfd2f0cc..1813a6c8dd 100644 --- a/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json +++ b/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json @@ -301,9 +301,15 @@ }, "AlertIncident": { "additionalProperties": false, - "description": "A period during which an alert rule's condition held: opened after enough consecutive breaches, resolved by the scheduler after enough consecutive healthy checks. Read-only via the API.", + "description": "A period during which an alert rule's condition held: opened after enough consecutive breaches, resolved by the scheduler after enough consecutive healthy checks. An open incident can be acknowledged to pause reminders; resolution is scheduler-driven.", "examples": [ { + "acknowledged_at": "2026-07-15T09:14:02.000Z", + "acknowledged_by": { + "name": "Dana Kim", + "user_id": "user_2Nk8mXqPfR3yZ1aB4cD5eF6g" + }, + "acknowledged_via": "slack", "comparator": "gt", "dedupe_key": "alrt_gU26thvJECdQvu54Ad9jiz:__total__", "error_issue_id": null, @@ -330,6 +336,21 @@ } ], "properties": { + "acknowledged_at": { + "description": "When someone acknowledged this open incident, or `null`. While acknowledged, reminders (`renotify`) pause; an escalation to a higher severity still notifies and clears the acknowledgement. Cleared when the incident resolves.", + "type": "string" + }, + "acknowledged_by": { + "$ref": "#/components/schemas/AlertIncidentActor", + "description": "Who acknowledged the incident, or `null` when it is not acknowledged." + }, + "acknowledged_via": { + "$ref": "#/components/schemas/_maple_AlertIncidentAckSource", + "description": "Where the acknowledgement came from (`web`, `api`, `mcp`, `slack`, `discord`), or `null` when not acknowledged.", + "examples": [ + "slack" + ] + }, "comparator": { "$ref": "#/components/schemas/_maple_AlertComparator" }, @@ -445,6 +466,24 @@ "title": "Alert Incident", "type": "object" }, + "AlertIncidentActor": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Display name as the source reported it, or `null` when none was available.", + "examples": [ + "Dana Kim" + ], + "type": "string" + }, + "user_id": { + "$ref": "#/components/schemas/_maple_UserId", + "description": "The Maple user, or `null` for a chat account not linked to one." + } + }, + "title": "Alert incident actor", + "type": "object" + }, "AlertIncidentList": { "additionalProperties": false, "description": "A cursor-paginated page of alert incidents.", @@ -6859,6 +6898,17 @@ "title": "Alert Event Type", "type": "string" }, + "_maple_AlertIncidentAckSource": { + "enum": [ + "web", + "api", + "mcp", + "slack", + "discord" + ], + "title": "Alert Incident Acknowledgement Source", + "type": "string" + }, "_maple_AlertIncidentHoldReason": { "enum": [ "no_data", @@ -7478,6 +7528,22 @@ "schema": { "$ref": "#/components/schemas/_maple_AlertRuleId" } + }, + { + "in": "query", + "name": "acknowledged", + "required": false, + "schema": { + "description": "`true` for open incidents someone has acknowledged, `false` for open incidents nobody has. Implies `status=open`.", + "enum": [ + "true", + "false" + ], + "examples": [ + "false" + ], + "type": "string" + } } ], "responses": { @@ -7613,7 +7679,7 @@ } } }, - "description": "A period during which an alert rule's condition held: opened after enough consecutive breaches, resolved by the scheduler after enough consecutive healthy checks. Read-only via the API." + "description": "A period during which an alert rule's condition held: opened after enough consecutive breaches, resolved by the scheduler after enough consecutive healthy checks. An open incident can be acknowledged to pause reminders; resolution is scheduler-driven." }, "400": { "content": { @@ -13982,7 +14048,7 @@ "name": "Alert Destinations" }, { - "description": "The incident history produced by your alert rules. Incidents open when a rule breaches for enough consecutive checks and resolve automatically once the signal recovers — this surface is read-only.", + "description": "The incident history produced by your alert rules. Incidents open when a rule breaches for enough consecutive checks and resolve automatically once the signal recovers. An open incident can be acknowledged to pause its reminders.", "name": "Alert Incidents" }, { From 7ec39ded6273da0cea9d2c716f47ee560d9cbf65 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 01:45:41 +0100 Subject: [PATCH 04/29] fix(alerts): keep the acknowledged filter on the incident list's next page, list the newest events --- apps/ai/src/mcp/tools/list-alert-incidents.ts | 1 + .../backend/src/services/alerts/AlertIncidentAckService.ts | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/ai/src/mcp/tools/list-alert-incidents.ts b/apps/ai/src/mcp/tools/list-alert-incidents.ts index f2b1b675c3..054ed051c3 100644 --- a/apps/ai/src/mcp/tools/list-alert-incidents.ts +++ b/apps/ai/src/mcp/tools/list-alert-incidents.ts @@ -236,6 +236,7 @@ export function registerListAlertIncidentsTool(server: McpToolRegistrar) { status: output.status, severity: output.severity, group_key: output.groupKey, + acknowledged: output.acknowledged, limit: MAX_LIMIT, }, "more incidents", diff --git a/packages/backend/src/services/alerts/AlertIncidentAckService.ts b/packages/backend/src/services/alerts/AlertIncidentAckService.ts index 5655e744ad..00617553df 100644 --- a/packages/backend/src/services/alerts/AlertIncidentAckService.ts +++ b/packages/backend/src/services/alerts/AlertIncidentAckService.ts @@ -191,13 +191,14 @@ export class AlertIncidentAckService extends Context.Service< PG.from(AlertIncidentEvents) .select() .where(($) => [$.orgId.eq(orgId), $.incidentId.eq(incidentId)]) - .orderBy(["createdAt", "asc"], ["id", "asc"]) + // The newest 500, put back in order below. + .orderBy(["createdAt", "desc"], ["id", "desc"]) .limit(500), ), ) // A type this build does not know (a newer writer) is skipped rather than failing the page. return Arr.getSomes( - Arr.map(rows, (row) => + Arr.map(Arr.reverse(rows), (row) => Option.map( decodeEventType(row.type), (type) => From 9eaf37ecbd9a2486e2560364471d9ff71a3517e1 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 01:49:47 +0100 Subject: [PATCH 05/29] chore(domain): regenerate anticipated errors for AlertIncidentResolvedError --- packages/domain/src/generated/anticipated-error-identifiers.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/domain/src/generated/anticipated-error-identifiers.ts b/packages/domain/src/generated/anticipated-error-identifiers.ts index ae211d5656..441ce1dac5 100644 --- a/packages/domain/src/generated/anticipated-error-identifiers.ts +++ b/packages/domain/src/generated/anticipated-error-identifiers.ts @@ -19,6 +19,7 @@ export const ANTICIPATED_ERROR_IDENTIFIER_LIST: ReadonlyArray = [ "@maple/http/errors/AlertDestinationNotFoundError", "@maple/http/errors/AlertForbiddenError", "@maple/http/errors/AlertIncidentNotFoundError", + "@maple/http/errors/AlertIncidentResolvedError", "@maple/http/errors/AlertRecipientSelectionError", "@maple/http/errors/AlertRuleDestinationNotFoundError", "@maple/http/errors/AlertRuleNotFoundError", From 6577df528259e6843ac9ba8d8ae80d0c13beb67f Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 02:27:04 +0100 Subject: [PATCH 06/29] fix(alerts): leave the internal incident id out of the resolved-incident message --- packages/backend/src/services/alerts/AlertIncidentAckService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/alerts/AlertIncidentAckService.ts b/packages/backend/src/services/alerts/AlertIncidentAckService.ts index 00617553df..4dc8e751ba 100644 --- a/packages/backend/src/services/alerts/AlertIncidentAckService.ts +++ b/packages/backend/src/services/alerts/AlertIncidentAckService.ts @@ -172,7 +172,7 @@ export class AlertIncidentAckService extends Context.Service< const current = yield* findIncident(orgId, incidentId) if (current.status !== "open") { return yield* new AlertIncidentResolvedError({ - message: `Alert incident '${incidentId}' is resolved; only an open incident can be ${acknowledged ? "acknowledged" : "unacknowledged"}.`, + message: `This alert incident is resolved; only an open incident can be ${acknowledged ? "acknowledged" : "unacknowledged"}.`, incidentId, }) } From 5abdc40680a215039b2cad540819528dcc0138c5 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 02:40:26 +0100 Subject: [PATCH 07/29] fix(alerts): attribute MCP acks like the audit log, stamp ack_cleared at the write --- .../tools/acknowledge-alert-incident.test.ts | 20 +++++++++++++++++++ .../mcp/tools/acknowledge-alert-incident.ts | 11 +++++++++- .../src/services/alerts/AlertsService.ts | 5 ++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts b/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts index 7e0e0a3295..e992aef5bb 100644 --- a/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts +++ b/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts @@ -7,6 +7,7 @@ import { cleanupTestDbs, createTestDb, executeSql, type TestDb } from "@maple/ba import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { CurrentMcpTenant } from "../lib/query-warehouse" import { registerAcknowledgeAlertIncidentTool } from "./acknowledge-alert-incident" +import { CurrentAuditActor } from "@maple/backend/services/auth/audit-actor" import type { McpToolError, McpToolRegistrar } from "./types" import type { McpToolRequirements } from "./runtime-requirements" import { unimplementedMcpToolServices } from "../../../test/mcp/unimplemented-tool-services" @@ -126,4 +127,23 @@ describe("acknowledge_alert_incident", () => { }) }).pipe(Effect.provide(layerFor(testDb))) }) + + it.effect("records Maple, not the token's placeholder user, when Maple itself acknowledges", () => { + const testDb = createTestDb(trackedDbs) + const call = acknowledgeTool() + return Effect.gen(function* () { + yield* seed(testDb) + const acked = yield* call({ incident_id: OPEN }).pipe( + Effect.provideService(CurrentAuditActor, { type: "system", source: "system" }), + ) + assert.deepInclude(acked, { acknowledged: true, acknowledgedBy: "Maple" }) + const rows = yield* Effect.promise(() => + testDb.pglite.query<{ acknowledged_by_user_id: string | null }>( + "select acknowledged_by_user_id from alert_incidents where id = $1", + [OPEN], + ), + ) + assert.isNull(rows.rows[0]?.acknowledged_by_user_id) + }).pipe(Effect.provide(layerFor(testDb))) + }) }) diff --git a/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts index 8b4a07bb3a..2e3416faec 100644 --- a/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts +++ b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts @@ -2,6 +2,8 @@ import { McpInvalidInputError, McpQueryError, type McpToolRegistrar } from "./ty import { Effect, Schema } from "effect" import { AcknowledgeAlertIncidentOutput } from "@maple/domain/mcp-outputs" import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" +import { auditAttribution } from "@maple/backend/services/audit/audit-access" +import { CurrentAuditActor } from "@maple/backend/services/auth/audit-actor" import { CurrentMcpTenant } from "../lib/query-warehouse" import { ackLabel } from "../lib/alert-rules" import * as P from "../lib/params" @@ -27,7 +29,14 @@ export function registerAcknowledgeAlertIncidentTool(server: McpToolRegistrar) { const tenant = yield* CurrentMcpTenant const acks = yield* AlertIncidentAckService const acknowledge = params.acknowledged !== false - const actor = { userId: tenant.userId, name: null, source: "mcp" as const } + // Attributed as the audit log attributes this call: Maple itself and a chat connector's + // placeholder carry no user, an agent is named by its client. + const attribution = auditAttribution(tenant, yield* CurrentAuditActor) + const actor = { + userId: attribution.actor.userId ?? null, + name: attribution.actor.label ?? (attribution.actor.type === "system" ? "Maple" : null), + source: "mcp" as const, + } const result = yield* ( acknowledge diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 81c30b2a8b..8c03a48b72 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -3322,6 +3322,9 @@ export class AlertsService extends Context.Service 0) { + // Stamped at the write, not the tick's start: an ack that committed during + // the evaluation must not sort after the event that cleared it. + const clearedAt = Math.max(timestamp, yield* now) yield* db.run( PG.insertInto(AlertIncidentEvents).values({ id: decodeAlertIncidentEventIdSync(makeUuid()), @@ -3331,7 +3334,7 @@ export class AlertsService extends Context.Service Date: Fri, 6 Nov 2026 02:40:54 +0100 Subject: [PATCH 08/29] chore(alerts): yield before the call --- apps/ai/src/mcp/tools/acknowledge-alert-incident.ts | 3 ++- packages/backend/src/services/alerts/AlertsService.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts index 2e3416faec..bbe82483a7 100644 --- a/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts +++ b/apps/ai/src/mcp/tools/acknowledge-alert-incident.ts @@ -31,7 +31,8 @@ export function registerAcknowledgeAlertIncidentTool(server: McpToolRegistrar) { const acknowledge = params.acknowledged !== false // Attributed as the audit log attributes this call: Maple itself and a chat connector's // placeholder carry no user, an agent is named by its client. - const attribution = auditAttribution(tenant, yield* CurrentAuditActor) + const credential = yield* CurrentAuditActor + const attribution = auditAttribution(tenant, credential) const actor = { userId: attribution.actor.userId ?? null, name: attribution.actor.label ?? (attribution.actor.type === "system" ? "Maple" : null), diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 8c03a48b72..1bf79102a1 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -3324,7 +3324,8 @@ export class AlertsService extends Context.Service 0) { // Stamped at the write, not the tick's start: an ack that committed during // the evaluation must not sort after the event that cleared it. - const clearedAt = Math.max(timestamp, yield* now) + const writeTime = yield* now + const clearedAt = Math.max(timestamp, writeTime) yield* db.run( PG.insertInto(AlertIncidentEvents).values({ id: decodeAlertIncidentEventIdSync(makeUuid()), From a929a95182eadcfbf4ad90be79df566b0ff8fbf3 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 12:47:41 +0100 Subject: [PATCH 09/29] feat(alerts): escalation chains that page step by step until someone acknowledges --- packages/alerting-core/src/escalation.test.ts | 180 + packages/alerting-core/src/escalation.ts | 96 + packages/alerting-core/src/index.ts | 1 + .../alerts/AlertDestinationsService.ts | 38 +- .../src/services/alerts/AlertRulesService.ts | 23 +- .../src/services/alerts/AlertsService.ts | 524 +- .../alerts/EscalationChainsService.ts | 366 + .../services/alerts/alert-delivery-rows.ts | 50 + .../services/alerts/alert-escalation-runs.ts | 293 + .../src/services/alerts/alert-thresholds.ts | 25 +- .../src/services/org/OrganizationService.ts | 4 + .../migration.sql | 52 + .../snapshot.json | 19340 ++++++++++++++++ packages/db/src/tables/escalation-chains.ts | 88 + packages/db/src/tables/index.ts | 4 + packages/domain/src/http/alerts.ts | 3 + packages/domain/src/http/escalation-chains.ts | 128 + packages/domain/src/http/index.ts | 1 + packages/primitives/src/index.ts | 4 + 19 files changed, 21057 insertions(+), 163 deletions(-) create mode 100644 packages/alerting-core/src/escalation.test.ts create mode 100644 packages/alerting-core/src/escalation.ts create mode 100644 packages/backend/src/services/alerts/EscalationChainsService.ts create mode 100644 packages/backend/src/services/alerts/alert-delivery-rows.ts create mode 100644 packages/backend/src/services/alerts/alert-escalation-runs.ts create mode 100644 packages/db/drizzle/20261106113702_escalation_chains/migration.sql create mode 100644 packages/db/drizzle/20261106113702_escalation_chains/snapshot.json create mode 100644 packages/db/src/tables/escalation-chains.ts create mode 100644 packages/domain/src/http/escalation-chains.ts diff --git a/packages/alerting-core/src/escalation.test.ts b/packages/alerting-core/src/escalation.test.ts new file mode 100644 index 0000000000..d517c438aa --- /dev/null +++ b/packages/alerting-core/src/escalation.test.ts @@ -0,0 +1,180 @@ +import { AlertDestinationId, type EscalationSteps } from "@maple/domain/http" +import { Schema } from "effect" +import { describe, expect, it } from "vitest" +import { + ESCALATION_SILENCE_RECHECK_MS, + type EscalationIncidentState, + type EscalationRunPosition, + escalationStepCount, + planEscalationAdvance, +} from "./escalation" + +const destinationIds = { + slack: "00000000-0000-4000-8000-000000000001", + pagerduty: "00000000-0000-4000-8000-000000000002", + email: "00000000-0000-4000-8000-000000000003", +} +const destination = (name: keyof typeof destinationIds) => ({ + type: "destination" as const, + destinationId: Schema.decodeSync(AlertDestinationId)(destinationIds[name]), +}) + +// Slack, wait 10; PagerDuty, wait 15; email, wait 15. +const steps: EscalationSteps = [ + { targets: [destination("slack")], waitMinutes: 10 }, + { targets: [destination("pagerduty")], waitMinutes: 15 }, + { targets: [destination("email")], waitMinutes: 15 }, +] + +const T0 = Date.UTC(2026, 10, 6, 14, 0) +const minutes = (n: number) => T0 + n * 60_000 + +const run = (overrides: Partial = {}): EscalationRunPosition => ({ + status: "active", + steps, + repeatCount: 0, + stepIndex: 0, + passIndex: 0, + nextStepAt: T0, + ...overrides, +}) + +const firing: EscalationIncidentState = { open: true, acknowledged: false, silenced: false } + +describe("planEscalationAdvance", () => { + it("sends the due step and schedules the next one its wait later", () => { + const plan = planEscalationAdvance(run(), firing, T0) + expect(plan).toMatchObject({ + kind: "send", + stepIndex: 0, + passIndex: 0, + step: { targets: [{ destinationId: destinationIds.slack }] }, + next: { kind: "step", stepIndex: 1, passIndex: 0, nextStepAt: minutes(10) }, + }) + }) + + it("waits until the step is due", () => { + expect( + planEscalationAdvance(run({ stepIndex: 1, nextStepAt: minutes(10) }), firing, minutes(9)), + ).toEqual({ + kind: "wait", + }) + }) + + it("counts the wait from when a late step was actually sent", () => { + const plan = planEscalationAdvance( + run({ stepIndex: 1, nextStepAt: minutes(10) }), + firing, + minutes(11), + ) + expect(plan).toMatchObject({ kind: "send", next: { nextStepAt: minutes(26) } }) + }) + + it("walks the email example: page at 14:10, stop at the 14:12 ack, never send step 3", () => { + const atTen = planEscalationAdvance( + run({ stepIndex: 1, nextStepAt: minutes(10) }), + firing, + minutes(10), + ) + expect(atTen).toMatchObject({ + kind: "send", + step: { targets: [{ destinationId: destinationIds.pagerduty }] }, + }) + const acked = { ...firing, acknowledged: true } + expect( + planEscalationAdvance(run({ stepIndex: 2, nextStepAt: minutes(25) }), acked, minutes(12)), + ).toEqual({ + kind: "pause", + }) + expect( + planEscalationAdvance( + run({ status: "acknowledged", stepIndex: 2, nextStepAt: minutes(25) }), + acked, + minutes(30), + ), + ).toEqual({ kind: "wait" }) + }) + + it("resumes an unacknowledged run where it stopped", () => { + const paused = run({ status: "acknowledged", stepIndex: 2, nextStepAt: minutes(25) }) + expect(planEscalationAdvance(paused, firing, minutes(20))).toEqual({ kind: "resume" }) + expect(planEscalationAdvance(paused, firing, minutes(40))).toMatchObject({ + kind: "send", + stepIndex: 2, + }) + }) + + it("ends without sending once the incident resolves, even mid-wait", () => { + const resolved = { ...firing, open: false } + expect(planEscalationAdvance(run({ nextStepAt: minutes(30) }), resolved, minutes(1))).toEqual({ + kind: "end", + status: "resolved", + }) + }) + + it("holds a due step through a silence and sends it once the silence ends", () => { + const silenced = { ...firing, silenced: true } + const due = run({ stepIndex: 1, nextStepAt: minutes(10) }) + expect(planEscalationAdvance(due, silenced, minutes(10))).toEqual({ + kind: "defer", + nextStepAt: minutes(10) + ESCALATION_SILENCE_RECHECK_MS, + }) + expect( + planEscalationAdvance(run({ stepIndex: 1, nextStepAt: minutes(30) }), firing, minutes(30)), + ).toMatchObject({ + kind: "send", + stepIndex: 1, + }) + }) + + it("repeats the whole chain, then reports it spent after the last step of the last pass", () => { + const repeating = { repeatCount: 1 } + expect(planEscalationAdvance(run({ ...repeating, stepIndex: 2 }), firing, T0)).toMatchObject({ + kind: "send", + next: { kind: "step", stepIndex: 0, passIndex: 1, nextStepAt: minutes(15) }, + }) + expect( + planEscalationAdvance(run({ ...repeating, stepIndex: 2, passIndex: 1 }), firing, T0), + ).toMatchObject({ + kind: "send", + stepIndex: 2, + passIndex: 1, + next: { kind: "exhausted" }, + }) + }) + + it("ends a run whose position is past its chain instead of sending", () => { + expect(planEscalationAdvance(run({ stepIndex: 7 }), firing, T0)).toEqual({ + kind: "end", + status: "exhausted", + }) + }) + + it("leaves ended runs alone", () => { + expect(planEscalationAdvance(run({ status: "exhausted" }), firing, minutes(90))).toEqual({ + kind: "wait", + }) + expect(planEscalationAdvance(run({ status: "superseded" }), { ...firing, open: false }, T0)).toEqual({ + kind: "wait", + }) + }) + + it("sends every step of every pass exactly once when nobody acknowledges", () => { + const walk = (position: EscalationRunPosition, nowMs: number, sent: number): number => { + const plan = planEscalationAdvance(position, firing, nowMs) + if (plan.kind !== "send") return sent + if (plan.next.kind === "exhausted") return sent + 1 + return walk( + { + ...position, + stepIndex: plan.next.stepIndex, + passIndex: plan.next.passIndex, + nextStepAt: plan.next.nextStepAt, + }, + plan.next.nextStepAt, + sent + 1, + ) + } + expect(walk(run({ repeatCount: 2 }), T0, 0)).toBe(escalationStepCount(steps, 2)) + }) +}) diff --git a/packages/alerting-core/src/escalation.ts b/packages/alerting-core/src/escalation.ts new file mode 100644 index 0000000000..0cb70aa653 --- /dev/null +++ b/packages/alerting-core/src/escalation.ts @@ -0,0 +1,96 @@ +import type { AlertEscalationRunStatus, EscalationStep, EscalationSteps } from "@maple/domain/http" + +/** + * Escalation chains: an incident walks a chain's steps, each notifying its targets and waiting + * `waitMinutes` for an acknowledgement before the next. Pure; the scheduler applies the plan + * with a compare-and-set on (stepIndex, passIndex) so each step is sent at most once. + */ + +/** How long a silenced run waits before the scheduler looks at it again. */ +export const ESCALATION_SILENCE_RECHECK_MS = 60_000 + +export interface EscalationRunPosition { + readonly status: AlertEscalationRunStatus + readonly steps: EscalationSteps + readonly repeatCount: number + readonly stepIndex: number + readonly passIndex: number + readonly nextStepAt: number +} + +export interface EscalationIncidentState { + readonly open: boolean + readonly acknowledged: boolean + /** A silence covers the incident right now. */ + readonly silenced: boolean +} + +export type EscalationAdvancePlan = + /** Nothing to write: not due, or already paused for the same reason. */ + | { readonly kind: "wait" } + /** The incident resolved, or the position is past the chain: the run ends without sending. */ + | { readonly kind: "end"; readonly status: "resolved" | "exhausted" } + /** Someone acknowledged: the run holds its place until an unacknowledge. */ + | { readonly kind: "pause" } + /** Unacknowledged before the step was due: the run goes back to waiting. */ + | { readonly kind: "resume" } + /** Due but silenced: look again later, sending nothing. Leaves an acknowledged run active. */ + | { readonly kind: "defer"; readonly nextStepAt: number } + /** Send `step`, then move to the next position, or end when the chain is spent. */ + | { + readonly kind: "send" + readonly step: EscalationStep + readonly stepIndex: number + readonly passIndex: number + readonly next: + | { + readonly kind: "step" + readonly stepIndex: number + readonly passIndex: number + readonly nextStepAt: number + } + | { readonly kind: "exhausted" } + } + +const nextPosition = ( + run: EscalationRunPosition, + step: EscalationStep, + nowMs: number, +): Extract["next"] => { + const nextStepAt = nowMs + step.waitMinutes * 60_000 + if (run.stepIndex + 1 < run.steps.length) { + return { kind: "step", stepIndex: run.stepIndex + 1, passIndex: run.passIndex, nextStepAt } + } + if (run.passIndex < run.repeatCount) { + return { kind: "step", stepIndex: 0, passIndex: run.passIndex + 1, nextStepAt } + } + return { kind: "exhausted" } +} + +export const planEscalationAdvance = ( + run: EscalationRunPosition, + incident: EscalationIncidentState, + nowMs: number, +): EscalationAdvancePlan => { + if (run.status !== "active" && run.status !== "acknowledged") return { kind: "wait" } + if (!incident.open) return { kind: "end", status: "resolved" } + if (incident.acknowledged) return run.status === "acknowledged" ? { kind: "wait" } : { kind: "pause" } + const due = run.nextStepAt <= nowMs + if (run.status === "acknowledged" && !due) return { kind: "resume" } + if (!due) return { kind: "wait" } + if (incident.silenced) return { kind: "defer", nextStepAt: nowMs + ESCALATION_SILENCE_RECHECK_MS } + const step = run.steps[run.stepIndex] + // A position past the snapshot cannot be sent; treat the chain as spent. + if (step === undefined) return { kind: "end", status: "exhausted" } + return { + kind: "send", + step, + stepIndex: run.stepIndex, + passIndex: run.passIndex, + next: nextPosition(run, step, nowMs), + } +} + +/** Total steps a chain sends when nobody acknowledges. */ +export const escalationStepCount = (steps: EscalationSteps, repeatCount: number): number => + steps.length * (repeatCount + 1) diff --git a/packages/alerting-core/src/index.ts b/packages/alerting-core/src/index.ts index 1cf542e41f..767c12bff8 100644 --- a/packages/alerting-core/src/index.ts +++ b/packages/alerting-core/src/index.ts @@ -22,6 +22,7 @@ import type { } from "@maple/domain/http" export type { AlertComparator, AlertEventType, AlertEvaluationStatus } from "@maple/domain/http" export * from "./tiers" +export * from "./escalation" export interface AlertObservation { readonly value: number | null diff --git a/packages/backend/src/services/alerts/AlertDestinationsService.ts b/packages/backend/src/services/alerts/AlertDestinationsService.ts index 3ce9fe0d01..2b692e72ab 100644 --- a/packages/backend/src/services/alerts/AlertDestinationsService.ts +++ b/packages/backend/src/services/alerts/AlertDestinationsService.ts @@ -29,7 +29,13 @@ import { } from "@maple/domain/http" import * as Orm from "@maple-dev/effect-orm/database" import * as PG from "@maple-dev/effect-orm/postgres" -import { AlertDestinations, AlertLinearIssues, AlertRules, type AlertDestinationRow } from "@maple/db/tables" +import { + AlertDestinations, + AlertLinearIssues, + AlertRules, + EscalationChains, + type AlertDestinationRow, +} from "@maple/db/tables" import { Context, Effect, Layer, Match, Option, Redacted, Schema, Array as Arr, String as Str } from "effect" import { HttpClient } from "effect/http" import { encryptAes256Gcm, type EncryptedValue } from "@maple/backend/platform/Crypto" @@ -1047,17 +1053,43 @@ export class AlertDestinationsService extends Context.Service< ]), ) if (stillReferenced.length > 0) { - return { referencedBy: stillReferenced, deleted: [] } + return { referencedBy: stillReferenced, chainNames: [], deleted: [] } + } + // Chain writes validate their step destinations under the same lock. + const chains = yield* db.run( + PG.from(EscalationChains) + .select("name") + .where(($) => [ + $.orgId.eq(orgId), + PG.sql.cond`${$.stepsJson} @> ${encodeJson([ + { targets: [{ type: "destination", destinationId }] }, + ])}::jsonb`, + ]), + ) + if (chains.length > 0) { + return { + referencedBy: [], + chainNames: Arr.map(chains, (chain) => chain.name), + deleted: [], + } } const deleted = yield* db.run( PG.deleteFrom(AlertDestinations) .where(($) => [$.orgId.eq(orgId), $.id.eq(destinationId)]) .returning(() => ({ txid: currentTxid })), ) - return { referencedBy: [], deleted } + return { referencedBy: [], chainNames: [], deleted } }), ), ) + if (deleteResult.chainNames.length > 0) { + return yield* new AlertDestinationInUseError({ + message: `Destination is still used by escalation chains: ${deleteResult.chainNames.join(", ")}`, + destinationId, + ruleIds: [], + ruleNames: [], + }) + } if (deleteResult.referencedBy.length > 0) { return yield* new AlertDestinationInUseError({ message: `Destination is still used by alert rules: ${deleteResult.referencedBy diff --git a/packages/backend/src/services/alerts/AlertRulesService.ts b/packages/backend/src/services/alerts/AlertRulesService.ts index 66009f1b59..8c8cfc86b4 100644 --- a/packages/backend/src/services/alerts/AlertRulesService.ts +++ b/packages/backend/src/services/alerts/AlertRulesService.ts @@ -25,8 +25,9 @@ import { AlertRuleClaims, AlertRules, AlertRuleStates, + EscalationChains, } from "@maple/db/tables" -import { Array as Arr, Context, Effect, HashSet, Layer, Option, Schema } from "effect" +import { Array as Arr, Context, Effect, HashSet, Layer, Option, Result, Schema } from "effect" import type { MapleDb } from "@maple/db/client" import { Database, type DatabaseApi } from "@maple/backend/platform/DatabaseLive" import { makeDbExecute } from "@maple/backend/platform/db-execute" @@ -248,6 +249,26 @@ export const makeAlertRulePersistence = (options: { }) } } + // Chains likewise: their deletion scans rules for references under this lock. + const chainIds = Arr.dedupe( + Arr.filterMap(normalized.thresholds, (tier) => + tier.escalationChainId == null + ? Result.failVoid + : Result.succeed(tier.escalationChainId), + ), + ) + if (chainIds.length > 0) { + const chainRows = yield* db.run( + PG.from(EscalationChains) + .select("id") + .where(($) => [$.orgId.eq(orgId), $.id.in_(...chainIds)]), + ) + if (chainRows.length < chainIds.length) { + return yield* makeAlertValidationError( + "Alert rule references an unknown escalation chain", + ) + } + } if (normalized.enabled) { const activeRows = yield* db.run( PG.from(AlertRules) diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 1bf79102a1..bf9892fb30 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -32,7 +32,6 @@ import { AlertDeliveryRejectedError, AlertDeliveryTargetMissingError, AlertDestinationDecryptionError, - AlertDeliveryEventDocument, AlertDestinationDocument, AlertDestinationStoredConfigInvalidError, AlertEvaluationResult, @@ -40,6 +39,7 @@ import { AlertForbiddenError, AlertIncidentDocument, AlertIncidentEventId, + AlertEscalationRunId, AlertIncidentHoldReason, AlertIncidentStatus, type AlertDestinationNotFoundError, @@ -56,6 +56,7 @@ import { type AlertRulePreviewRequest, AlertSeverity as AlertSeveritySchema, type AlertSeverity, + type EscalationChainId, AlertSignalType as AlertSignalTypeSchema, AlertValidationError, AlertNotificationTemplate, @@ -141,7 +142,17 @@ import { import { ChatSessions } from "@maple/backend/platform/bindings" import { Database } from "@maple/backend/platform/DatabaseLive" import { formatComparator } from "./alert-formatting" -import { ruleAtSeverity } from "./alert-thresholds" +import { escalationChainFor, ruleAtSeverity } from "./alert-thresholds" +import { + applyEscalationPlan, + enabledDestinationIds, + type EscalationWorkRow, + loadEscalationChain, + planEscalationWork, + selectEscalationWork, + startEscalationRun, +} from "./alert-escalation-runs" +import { queuedDeliveryEventRow } from "./alert-delivery-rows" import { buildChatDigestBlocks, CHAT_DIGEST_THRESHOLD, type ChatDigestItem } from "./delivery/chat-digest" import { makeIncidentPushBudget, type IncidentPushBudget } from "./alert-push-budget" import { EmailService } from "@maple/backend/platform/EmailService" @@ -435,8 +446,8 @@ const StoredDeliveryPayloadSchema = Schema.StructWithRest( const decodeAlertRuleIdSync = Schema.decodeUnknownSync(AlertRuleDocument.fields.id) const decodeAlertIncidentIdSync = Schema.decodeUnknownSync(AlertIncidentDocument.fields.id) -const decodeAlertDeliveryEventIdSync = Schema.decodeUnknownSync(AlertDeliveryEventDocument.fields.id) const decodeAlertIncidentEventIdSync = Schema.decodeUnknownSync(AlertIncidentEventId) +const decodeAlertEscalationRunIdSync = Schema.decodeUnknownSync(AlertEscalationRunId) /** An incident that leaves `open` stops being acknowledged; its history stays in `alert_incident_events`. */ const clearedAcknowledgement = { @@ -1182,29 +1193,20 @@ export class AlertsService extends Context.Service orm.run( PG.insertInto(AlertDeliveryEvents) - .values({ - id: decodeAlertDeliveryEventIdSync(makeUuid()), - orgId, - incidentId, - ruleId, - destinationId, - deliveryKey, - eventType, - attemptNumber, - status: "queued", - scheduledAt: scheduledAt, - claimedAt: null, - claimExpiresAt: null, - claimedBy: null, - attemptedAt: null, - providerMessage: null, - providerReference: null, - responseCode: null, - errorMessage: null, - payloadJson: payload, - createdAt: scheduledAt, - updatedAt: scheduledAt, - }) + .values( + queuedDeliveryEventRow({ + id: makeUuid(), + orgId, + incidentId, + ruleId, + destinationId, + eventType, + payload, + scheduledAt, + deliveryKey, + attemptNumber, + }), + ) .onConflictDoNothing(), ) @@ -1361,135 +1363,204 @@ export class AlertsService extends Context.Service - db.run( - PG.from(AlertDeliveryEvents) - .select("destinationId") - .where(($) => [$.orgId.eq(orgId), $.incidentId.eq(incident.id)]), - ), - )).map((row) => row.destinationId) - : [] - const destinationIds = Arr.dedupe([...rule.destinationIds, ...priorDestinationIds]) - if (destinationIds.length === 0) return [] - - const rows = yield* dbExecute((db) => - db.run( - PG.from(AlertDestinations) - .select("id", "enabled") - .where(($) => [$.orgId.eq(orgId), $.id.in_(...destinationIds)]), - ), - ) - - const destinations = new Map(rows.map((row) => [row.id, row])) - - // Once per notification, not once per destination: N destinations - // on one rule must not mean N reads of the same series. Never - // fails — `loadChartSeries` answers `null` for every problem it - // can have, so a slow or broken warehouse costs the sparkline and - // nothing else. - const window = chartWindow({ - incidentStartedAtMs: incident.firstTriggeredAt, - nowMs: scheduledAt, - windowMinutes: rule.windowMinutes, - }) - // An uptime rule's series is a 0/1 step that says less than the check's error, so it gets no chart. - const series = - rule.signalType === "uptime" - ? null - : yield* loadChartSeries(warehouse, systemTenant(orgId), { - orgId, - ruleId: rule.id, - groupKey: incident.groupKey, - comparator: rule.comparator, - threshold: rule.threshold, - ...window, - }) + /** + * An incident event's delivery rows and the payload they carry. `withPayload` builds the + * payload even for a rule with no destinations, for an escalation chain to resend. + */ + const prepareIncidentNotification = Effect.fn("AlertsService.prepareIncidentNotification")( + function* ( + orgId: OrgId, + rule: NormalizedRule, + incident: AlertIncidentRow, + evaluation: EvaluatedRule, + eventType: AlertEventTypeValue, + scheduledAt: number, + withPayload: boolean, + ) { + // A resolve also reaches every destination this incident already + // notified, so a rule edit that drops one cannot orphan its alert. + const priorDestinationIds = + eventType === "resolve" + ? (yield* dbExecute((db) => + db.run( + PG.from(AlertDeliveryEvents) + .select("destinationId") + .where(($) => [$.orgId.eq(orgId), $.incidentId.eq(incident.id)]), + ), + )).map((row) => row.destinationId) + : [] + const destinationIds = Arr.dedupe([...rule.destinationIds, ...priorDestinationIds]) + if (destinationIds.length === 0 && !withPayload) return { deliveries: [], payload: null } + + const rows = + destinationIds.length === 0 + ? [] + : yield* dbExecute((db) => + db.run( + PG.from(AlertDestinations) + .select("id", "enabled") + .where(($) => [$.orgId.eq(orgId), $.id.in_(...destinationIds)]), + ), + ) - // The image URL rides with the series it describes: both pin the - // same window, so a retry an hour later renders the same picture - // this notification was written about. - const display = resolveSignalDisplay(rule) - const displayGroup = incident.groupKey === "__total__" ? null : incident.groupKey - const chartUrl = - series === null - ? null - : chartImageUrl({ - appBaseUrl: env.MAPLE_APP_BASE_URL, - hmacKey: Option.match(env.MAPLE_SHARE_TOKEN_HMAC_KEY, { - onNone: () => null, - onSome: (key) => Redacted.value(key), - }), - orgId, - ruleId: rule.id, - groupKey: incident.groupKey, - ...window, - title: displayGroup ? `${display.label} · ${displayGroup}` : display.label, - unit: mapSignalUnit(display.unit), - threshold: series.threshold, - breachSide: series.breachSide, - }) + const destinations = new Map(rows.map((row) => [row.id, row])) - const payload = yield* buildPayload( - { - eventType, - incidentId: incident.id, - incidentStatus: decodeAlertIncidentStatusSync(incident.status), - dedupeKey: incident.dedupeKey, - ruleId: rule.id, - ruleName: rule.name, - groupKey: incident.groupKey, - signalType: rule.signalType, - severity: rule.severity, - // Snapshotted with the event: a retry or a late delivery - // must page the key this event was raised under. - ruleSeverities: rule.thresholds.map((tier) => tier.severity), - ...(incident.peakSeverity === null - ? undefined - : { incidentPeakSeverity: decodeAlertSeveritySync(incident.peakSeverity) }), - comparator: rule.comparator, - threshold: rule.threshold, - thresholdUpper: rule.thresholdUpper, + // Once per notification, not once per destination: N destinations + // on one rule must not mean N reads of the same series. Never + // fails — `loadChartSeries` answers `null` for every problem it + // can have, so a slow or broken warehouse costs the sparkline and + // nothing else. + const window = chartWindow({ + incidentStartedAtMs: incident.firstTriggeredAt, + nowMs: scheduledAt, windowMinutes: rule.windowMinutes, - value: evaluation.value, - sampleCount: evaluation.sampleCount, - // The evaluator puts the check's error in the reason of an uptime rule. - detail: rule.signalType === "uptime" ? evaluation.reason : null, - sparkline: series?.sparkline ?? null, - chartUrl, - template: rule.notificationTemplate, - linkUrl: resolveNotificationLinkUrl(rule, incident.groupKey, incident.id), - sentAtMs: scheduledAt, - }, - orgId, - ).pipe(Effect.mapError((error) => makeValidationError(error.message, [], error))) + }) + // An uptime rule's series is a 0/1 step that says less than the check's error, so it gets no chart. + const series = + rule.signalType === "uptime" + ? null + : yield* loadChartSeries(warehouse, systemTenant(orgId), { + orgId, + ruleId: rule.id, + groupKey: incident.groupKey, + comparator: rule.comparator, + threshold: rule.threshold, + ...window, + }) - return destinationIds.flatMap((destinationId): ReadonlyArray => { - const destination = destinations.get(destinationId) - if (!destination || !destination.enabled) return [] - return [ + // The image URL rides with the series it describes: both pin the + // same window, so a retry an hour later renders the same picture + // this notification was written about. + const display = resolveSignalDisplay(rule) + const displayGroup = incident.groupKey === "__total__" ? null : incident.groupKey + const chartUrl = + series === null + ? null + : chartImageUrl({ + appBaseUrl: env.MAPLE_APP_BASE_URL, + hmacKey: Option.match(env.MAPLE_SHARE_TOKEN_HMAC_KEY, { + onNone: () => null, + onSome: (key) => Redacted.value(key), + }), + orgId, + ruleId: rule.id, + groupKey: incident.groupKey, + ...window, + title: displayGroup + ? `${display.label} · ${displayGroup}` + : display.label, + unit: mapSignalUnit(display.unit), + threshold: series.threshold, + breachSide: series.breachSide, + }) + + const payload = yield* buildPayload( { - orgId, + eventType, incidentId: incident.id, + incidentStatus: decodeAlertIncidentStatusSync(incident.status), + dedupeKey: incident.dedupeKey, ruleId: rule.id, - destinationId, - eventType, - payload, - scheduledAt, + ruleName: rule.name, + groupKey: incident.groupKey, + signalType: rule.signalType, + severity: rule.severity, + // Snapshotted with the event: a retry or a late delivery + // must page the key this event was raised under. + ruleSeverities: rule.thresholds.map((tier) => tier.severity), + ...(incident.peakSeverity === null + ? undefined + : { incidentPeakSeverity: decodeAlertSeveritySync(incident.peakSeverity) }), + comparator: rule.comparator, + threshold: rule.threshold, + thresholdUpper: rule.thresholdUpper, + windowMinutes: rule.windowMinutes, + value: evaluation.value, + sampleCount: evaluation.sampleCount, + // The evaluator puts the check's error in the reason of an uptime rule. + detail: rule.signalType === "uptime" ? evaluation.reason : null, + sparkline: series?.sparkline ?? null, + chartUrl, + template: rule.notificationTemplate, + linkUrl: resolveNotificationLinkUrl(rule, incident.groupKey, incident.id), + sentAtMs: scheduledAt, }, - ] - }) + orgId, + ).pipe(Effect.mapError((error) => makeValidationError(error.message, [], error))) + + const deliveries = destinationIds.flatMap( + (destinationId): ReadonlyArray => { + const destination = destinations.get(destinationId) + if (!destination || !destination.enabled) return [] + return [ + { + orgId, + incidentId: incident.id, + ruleId: rule.id, + destinationId, + eventType, + payload, + scheduledAt, + }, + ] + }, + ) + return { deliveries, payload } + }, + ) + + const prepareIncidentDeliveries = ( + orgId: OrgId, + rule: NormalizedRule, + incident: AlertIncidentRow, + evaluation: EvaluatedRule, + eventType: AlertEventTypeValue, + scheduledAt: number, + ) => + prepareIncidentNotification( + orgId, + rule, + incident, + evaluation, + eventType, + scheduledAt, + false, + ).pipe(Effect.map((notification) => notification.deliveries)) + + /** + * `chainId`'s chain for an incident at `severity`, with the trigger payload its steps resend. + * Null when there is no chain or it is gone. Read before the claim write. + */ + const prepareEscalationStart = Effect.fn("AlertsService.prepareEscalationStart")(function* ( + chainId: EscalationChainId | null, + orgId: OrgId, + rule: NormalizedRule, + severity: AlertSeverity, + incident: AlertIncidentRow, + evaluation: EvaluatedRule, + scheduledAt: number, + prepared: { + readonly deliveries: ReadonlyArray + readonly payload: Record | null + } | null, + ) { + if (chainId === null) return null + const chain = yield* dbExecute((db) => loadEscalationChain(db, orgId, chainId)) + if (chain === null) return null + const payload = + prepared?.payload ?? + (yield* prepareIncidentNotification( + orgId, + ruleAtSeverity(rule, severity), + incident, + evaluation, + "trigger", + scheduledAt, + true, + )).payload + if (payload === null) return null + yield* Effect.annotateCurrentSpan({ "maple.escalation.chain_id": chain.id }) + return { chain, payload, severity } }) const insertIncidentDeliveries = ( @@ -3154,18 +3225,34 @@ export class AlertsService extends Context.Service rows.length === 0 ? Effect.void - : insertIncidentDeliveries(db, deliveries), + : Effect.andThen( + insertIncidentDeliveries(db, deliveries), + escalation === null + ? Effect.void + : startEscalationRun(db, { + id: decodeAlertEscalationRunIdSync( + makeUuid(), + ), + orgId: row.orgId, + incidentId, + ...escalation, + nowMs: timestamp, + }), + ), ), ), ) @@ -3279,6 +3379,27 @@ export class AlertsService extends Context.Service tier.severity === severity, + ), + severity, + ), + row.orgId, + normalized, + severity, + refreshedIncident, + evaluation, + timestamp, + null, + ) + : null // An escalation clears whatever ack stands when it commits, decided in SQL rather // than from the prefetch, which a human ack or unack may have overtaken. const refreshed = yield* writeUnderClaim(claim, (db) => @@ -3311,6 +3432,15 @@ export class AlertsService extends Context.Service + enabledDestinationIds( + db, + row.orgId, + Arr.map(plan.step.targets, (target) => target.destinationId), + ), + ) + : new Set() + const outcome = yield* dbExecute((db) => + db.transaction( + applyEscalationPlan(db, { row, plan, nowMs, enabled, makeDeliveryId: makeUuid }), + ), + ) + yield* Effect.annotateCurrentSpan({ + "maple.escalation.plan": plan.kind, + "maple.escalation.outcome": outcome, + ...(plan.kind === "send" + ? { + "maple.escalation.step": plan.stepIndex + 1, + "maple.escalation.pass": plan.passIndex + 1, + "maple.escalation.lateness_ms": nowMs - row.nextStepAt, + "maple.escalation.targets": enabled.size, + } + : undefined), + }) + return outcome + }) + + /** Sends every due escalation step, so the drain that follows delivers it this tick. */ + const advanceEscalations = Effect.fn("AlertsService.advanceEscalations")(function* () { + const nowMs = yield* now + const work = yield* dbExecute((db) => selectEscalationWork(db, nowMs)) + const outcomes = yield* Effect.forEach( + work, + (row) => + advanceEscalationRun(row, nowMs).pipe( + Effect.catchCause((cause) => + Effect.logError("Escalation step failed").pipe( + Effect.annotateLogs({ + runId: row.id, + incidentId: row.incidentId, + cause: summarizeCause(cause), + }), + Effect.as("failed" as const), + ), + ), + ), + { concurrency: 4 }, + ) + const sent = Arr.filter(outcomes, (outcome) => outcome === "sent").length + yield* Effect.annotateCurrentSpan({ + "maple.escalation.runs": work.length, + "maple.escalation.sent": sent, + "maple.escalation.failed": Arr.filter(outcomes, (outcome) => outcome === "failed").length, + }) + return sent + }) + // Deliveries drain even when evaluation fails, so a broken rule pass // never holds back notifications already queued. The failure is raised // after the drain. @@ -5318,6 +5521,11 @@ export class AlertsService extends Context.Service Option.match(fiber, { onNone: () => Effect.succeed(0), onSome: Fiber.join }), ) + // The standard lane also advances escalation chains, after evaluation (which may have + // resolved an incident) and before the drain (which then sends the step this tick). + if (lane === "standard") { + yield* advanceEscalations().pipe(isolate("Escalation advance failed", {})) + } // One lane drains: the standard lane runs every minute, and its drain has its own 20s budget. const delivery = yield* Effect.exit( lane === "standard" diff --git a/packages/backend/src/services/alerts/EscalationChainsService.ts b/packages/backend/src/services/alerts/EscalationChainsService.ts new file mode 100644 index 0000000000..17183d25c9 --- /dev/null +++ b/packages/backend/src/services/alerts/EscalationChainsService.ts @@ -0,0 +1,366 @@ +import { + AlertForbiddenError, + AlertPersistenceError, + AlertRuleId as AlertRuleIdSchema, + AlertValidationError, + EscalationChainDocument, + EscalationChainId as EscalationChainIdSchema, + EscalationChainInUseError, + EscalationChainNotFoundError, + EscalationChainsListResponse, + IsoDateTimeString, + type AlertDestinationId, + type AlertRuleId, + type EscalationChainCreateRequest, + type EscalationChainId, + type EscalationChainUpdateRequest, + type EscalationSteps, + type OrgId, + type RoleName, + type UserId, +} from "@maple/domain/http" +import * as Orm from "@maple-dev/effect-orm/database" +import * as PG from "@maple-dev/effect-orm/postgres" +import { AlertDestinations, AlertRules, EscalationChains, type EscalationChainRow } from "@maple/db/tables" +import type { MapleDb } from "@maple/db/client" +import { Array as Arr, Context, Effect, Layer, Option, Result, Schema } from "effect" +import { Database } from "@maple/backend/platform/DatabaseLive" +import { makeDbExecute } from "@maple/backend/platform/db-execute" +import * as Policy from "@maple/domain/policy" +import { makePersistenceError } from "./alert-persistence" +import { AlertRuntime } from "./AlertRuntime" + +/** Chains an org may keep; a chain is reusable across rules, so few are needed. */ +const MAX_CHAINS_PER_ORG = 100 + +const decodeChainIdSync = Schema.decodeUnknownSync(EscalationChainIdSchema) +const decodeRuleIdSync = Schema.decodeUnknownSync(AlertRuleIdSchema) +// Only the field read here, leniently: a tier list in an older or newer shape still names its chains. +const decodeTierChainIds = Schema.decodeUnknownOption( + Schema.Array(Schema.Struct({ escalationChainId: Schema.optionalKey(Schema.NullOr(Schema.String)) })), +) + +export interface EscalationChainsServiceApi { + readonly list: (orgId: OrgId) => Effect.Effect + readonly get: ( + orgId: OrgId, + chainId: EscalationChainId, + ) => Effect.Effect + readonly create: ( + orgId: OrgId, + userId: UserId, + roles: ReadonlyArray, + request: EscalationChainCreateRequest, + ) => Effect.Effect< + EscalationChainDocument, + AlertForbiddenError | AlertValidationError | AlertPersistenceError + > + readonly update: ( + orgId: OrgId, + userId: UserId, + roles: ReadonlyArray, + chainId: EscalationChainId, + request: EscalationChainUpdateRequest, + ) => Effect.Effect< + EscalationChainDocument, + AlertForbiddenError | AlertValidationError | AlertPersistenceError | EscalationChainNotFoundError + > + /** Refused while a rule's tier names the chain; escalations already running finish from their snapshot. */ + readonly delete: ( + orgId: OrgId, + roles: ReadonlyArray, + chainId: EscalationChainId, + ) => Effect.Effect< + void, + AlertForbiddenError | AlertPersistenceError | EscalationChainNotFoundError | EscalationChainInUseError + > +} + +const validationError = (message: string) => new AlertValidationError({ message, details: [message] }) + +const decodeIsoSync = Schema.decodeUnknownSync(IsoDateTimeString) +const iso = (ms: number) => decodeIsoSync(new Date(ms).toISOString()) + +interface RuleRef { + readonly id: AlertRuleId + readonly name: string +} + +/** Which rules name each chain, from every rule's tier list. */ +const ruleRefsByChain = (db: MapleDb, orgId: OrgId) => + db + .run( + PG.from(AlertRules) + .select(($) => ({ id: $.id, name: $.name, thresholdsJson: PG.undecoded($.thresholdsJson) })) + .where(($) => [$.orgId.eq(orgId), $.thresholdsJson.isNotNull()]), + ) + .pipe( + Effect.map((rows) => { + const refs = new Map>() + Arr.forEach(rows, (row) => { + const chainIds = Option.match(decodeTierChainIds(row.thresholdsJson), { + onNone: () => [], + onSome: (tiers) => + Arr.dedupe( + Arr.filterMap(tiers, (tier) => + tier.escalationChainId == null + ? Result.failVoid + : Result.succeed(tier.escalationChainId), + ), + ), + }) + Arr.forEach(chainIds, (chainId) => { + refs.set(chainId, [ + ...(refs.get(chainId) ?? []), + { id: decodeRuleIdSync(row.id), name: row.name }, + ]) + }) + }) + return refs + }), + ) + +const stepDestinationIds = (steps: EscalationSteps): ReadonlyArray => + Arr.dedupe(Arr.flatMap(steps, (step) => Arr.map(step.targets, (target) => target.destinationId))) + +const toDocument = (row: EscalationChainRow, refs: ReadonlyArray) => + new EscalationChainDocument({ + id: row.id, + name: row.name, + description: row.description, + steps: row.stepsJson, + repeatCount: row.repeatCount, + ruleIds: Arr.map(refs, (ref) => ref.id), + createdAt: iso(row.createdAt), + updatedAt: iso(row.updatedAt), + }) + +export class EscalationChainsService extends Context.Service< + EscalationChainsService, + EscalationChainsServiceApi +>()("@maple/backend/services/alerts/EscalationChainsService", { + make: Effect.gen(function* () { + const database = yield* Database + const runtime = yield* AlertRuntime + const dbExecute = makeDbExecute(database, "EscalationChainsService", makePersistenceError) + + const requireManage = (roles: ReadonlyArray) => + Policy.check( + roles, + "alerts:manage", + (denial) => + new AlertForbiddenError({ + message: denial.message, + ...(roles.length > 0 ? { roles: [...roles] } : undefined), + }), + ) + + const findRow = (db: MapleDb, orgId: OrgId, chainId: EscalationChainId) => + db + .run( + PG.from(EscalationChains) + .select() + .where(($) => [$.orgId.eq(orgId), $.id.eq(chainId)]) + .limit(1), + ) + .pipe( + Effect.flatMap((rows) => + Arr.match(rows, { + onEmpty: () => + Effect.fail( + new EscalationChainNotFoundError({ + message: "Escalation chain not found", + chainId, + }), + ), + onNonEmpty: ([row]) => Effect.succeed(row), + }), + ), + ) + + /** Under the org's advisory lock, which destination deletion also takes before its reference scan. */ + const lockOrg = (db: MapleDb, orgId: OrgId) => + db.execute(Orm.sql`select pg_advisory_xact_lock(hashtext(${orgId}))`) + + const requireDestinations = (db: MapleDb, orgId: OrgId, steps: EscalationSteps) => + Effect.gen(function* () { + const ids = stepDestinationIds(steps) + const rows = yield* db.run( + PG.from(AlertDestinations) + .select("id") + .where(($) => [$.orgId.eq(orgId), $.id.in_(...ids)]), + ) + if (rows.length < ids.length) { + return yield* validationError( + "An escalation step names a destination that does not exist", + ) + } + }) + + const list = Effect.fn("EscalationChainsService.list")(function* (orgId: OrgId) { + const [rows, refs] = yield* dbExecute((db) => + Effect.all([ + db.run( + PG.from(EscalationChains) + .select() + .where(($) => [$.orgId.eq(orgId)]) + .orderBy(["name", "asc"]) + .limit(MAX_CHAINS_PER_ORG), + ), + ruleRefsByChain(db, orgId), + ]), + ) + return new EscalationChainsListResponse({ + chains: Arr.map(rows, (row) => toDocument(row, refs.get(row.id) ?? [])), + }) + }) + + const get = Effect.fn("EscalationChainsService.get")(function* ( + orgId: OrgId, + chainId: EscalationChainId, + ) { + const [row, refs] = yield* dbExecute((db) => + Effect.all([findRow(db, orgId, chainId), ruleRefsByChain(db, orgId)]), + ) + return toDocument(row, refs.get(row.id) ?? []) + }) + + const create = Effect.fn("EscalationChainsService.create")(function* ( + orgId: OrgId, + userId: UserId, + roles: ReadonlyArray, + request: EscalationChainCreateRequest, + ) { + yield* requireManage(roles) + const nowMs = yield* runtime.now + const id = decodeChainIdSync(runtime.makeUuid()) + const row = yield* dbExecute((db) => + db.transaction( + Effect.gen(function* () { + yield* lockOrg(db, orgId) + const existing = yield* db.run( + PG.from(EscalationChains) + .select("id") + .where(($) => [$.orgId.eq(orgId)]) + .limit(MAX_CHAINS_PER_ORG), + ) + if (existing.length >= MAX_CHAINS_PER_ORG) { + return yield* validationError( + `Organizations may have at most ${MAX_CHAINS_PER_ORG} escalation chains`, + ) + } + yield* requireDestinations(db, orgId, request.steps) + const [inserted] = yield* db.run( + PG.insertInto(EscalationChains) + .values({ + id, + orgId, + name: request.name, + description: request.description ?? null, + stepsJson: request.steps, + repeatCount: request.repeatCount ?? 0, + createdAt: nowMs, + updatedAt: nowMs, + createdBy: userId, + updatedBy: userId, + }) + .returning(), + ) + return inserted + }), + ), + ) + if (row === undefined) { + return yield* new AlertPersistenceError({ message: "Escalation chain was not created" }) + } + return toDocument(row, []) + }) + + const update = Effect.fn("EscalationChainsService.update")(function* ( + orgId: OrgId, + userId: UserId, + roles: ReadonlyArray, + chainId: EscalationChainId, + request: EscalationChainUpdateRequest, + ) { + yield* requireManage(roles) + const nowMs = yield* runtime.now + const [row, refs] = yield* dbExecute((db) => + db.transaction( + Effect.gen(function* () { + yield* lockOrg(db, orgId) + yield* findRow(db, orgId, chainId) + if (request.steps !== undefined) yield* requireDestinations(db, orgId, request.steps) + const [updated] = yield* db.run( + PG.update(EscalationChains) + .set({ + ...(request.name === undefined ? undefined : { name: request.name }), + ...(request.description === undefined + ? undefined + : { description: request.description }), + ...(request.steps === undefined + ? undefined + : { stepsJson: request.steps }), + ...(request.repeatCount === undefined + ? undefined + : { repeatCount: request.repeatCount }), + updatedAt: nowMs, + updatedBy: userId, + }) + .where(($) => [$.orgId.eq(orgId), $.id.eq(chainId)]) + .returning(), + ) + if (updated === undefined) { + return yield* new EscalationChainNotFoundError({ + message: "Escalation chain not found", + chainId, + }) + } + return [updated, yield* ruleRefsByChain(db, orgId)] as const + }), + ), + ) + return toDocument(row, refs.get(row.id) ?? []) + }) + + const remove = Effect.fn("EscalationChainsService.delete")(function* ( + orgId: OrgId, + roles: ReadonlyArray, + chainId: EscalationChainId, + ) { + yield* requireManage(roles) + yield* dbExecute((db) => + db.transaction( + Effect.gen(function* () { + // Rule writes check their chains under this lock, so no rule can commit a reference + // between this scan and the delete. + yield* lockOrg(db, orgId) + yield* findRow(db, orgId, chainId) + const refs = (yield* ruleRefsByChain(db, orgId)).get(chainId) ?? [] + if (refs.length > 0) { + return yield* new EscalationChainInUseError({ + message: `Escalation chain is still used by alert rules: ${Arr.join( + Arr.map(refs, (ref) => ref.name), + ", ", + )}`, + chainId, + ruleIds: Arr.map(refs, (ref) => ref.id), + ruleNames: Arr.map(refs, (ref) => ref.name), + }) + } + yield* db.run( + PG.deleteFrom(EscalationChains).where(($) => [ + $.orgId.eq(orgId), + $.id.eq(chainId), + ]), + ) + }), + ), + ) + }) + + return { list, get, create, update, delete: remove } satisfies EscalationChainsServiceApi + }), +}) { + static readonly layer = Layer.effect(this, this.make) +} diff --git a/packages/backend/src/services/alerts/alert-delivery-rows.ts b/packages/backend/src/services/alerts/alert-delivery-rows.ts new file mode 100644 index 0000000000..a979019f65 --- /dev/null +++ b/packages/backend/src/services/alerts/alert-delivery-rows.ts @@ -0,0 +1,50 @@ +import type { + AlertDestinationId, + AlertEventType, + AlertIncidentId, + AlertRuleId, + OrgId, +} from "@maple/domain/http" +import { AlertDeliveryEventDocument } from "@maple/domain/http" +import type { AlertDeliveryEventRow } from "@maple/db/tables" +import { Schema } from "effect" + +const decodeDeliveryEventId = Schema.decodeUnknownSync(AlertDeliveryEventDocument.fields.id) + +export interface QueuedDeliveryEvent { + readonly id: string + readonly orgId: OrgId + readonly incidentId: AlertIncidentId | null + readonly ruleId: AlertRuleId + readonly destinationId: AlertDestinationId + readonly eventType: AlertEventType + readonly payload: Record + readonly scheduledAt: number + readonly deliveryKey: string + readonly attemptNumber: number +} + +/** A delivery row waiting for the drain; every writer of the outbox builds its row here. */ +export const queuedDeliveryEventRow = (event: QueuedDeliveryEvent): AlertDeliveryEventRow => ({ + id: decodeDeliveryEventId(event.id), + orgId: event.orgId, + incidentId: event.incidentId, + ruleId: event.ruleId, + destinationId: event.destinationId, + deliveryKey: event.deliveryKey, + eventType: event.eventType, + attemptNumber: event.attemptNumber, + status: "queued", + scheduledAt: event.scheduledAt, + claimedAt: null, + claimExpiresAt: null, + claimedBy: null, + attemptedAt: null, + providerMessage: null, + providerReference: null, + responseCode: null, + errorMessage: null, + payloadJson: event.payload, + createdAt: event.scheduledAt, + updatedAt: event.scheduledAt, +}) diff --git a/packages/backend/src/services/alerts/alert-escalation-runs.ts b/packages/backend/src/services/alerts/alert-escalation-runs.ts new file mode 100644 index 0000000000..5254c955a6 --- /dev/null +++ b/packages/backend/src/services/alerts/alert-escalation-runs.ts @@ -0,0 +1,293 @@ +/** + * Escalation runs: one incident's walk through a chain. The alert scheduler starts a run in the + * transaction that opens or escalates an incident, and advances due runs between evaluation and + * the delivery drain, so a due step is sent in the same tick. Each step is a compare-and-set on the + * run's position committed with the step's delivery rows: at most one writer sends a given step. + */ +import { makeAlertDeliveryKey, planEscalationAdvance, type EscalationAdvancePlan } from "@maple/alerting-core" +import type { + AlertDestinationId, + AlertIncidentId, + AlertSeverity, + EscalationChainId, + EscalationSteps, + OrgId, +} from "@maple/domain/http" +import type { AlertEscalationRunId } from "@maple/domain/primitives" +import * as PG from "@maple-dev/effect-orm/postgres" +import { + AlertDeliveryEvents, + AlertDestinations, + AlertEscalationRuns, + AlertIncidents, + EscalationChains, +} from "@maple/db/tables" +import type { MapleDb } from "@maple/db/client" +import { Array as Arr, Effect } from "effect" +import { queuedDeliveryEventRow } from "./alert-delivery-rows" + +const LIVE_STATUSES = ["active", "acknowledged"] as const + +/** Runs one scheduler tick looks at; the rest wait a minute, oldest due first. */ +export const ESCALATION_RUNS_PER_TICK = 200 + +export interface EscalationChainSnapshot { + readonly id: EscalationChainId + readonly name: string + readonly steps: EscalationSteps + readonly repeatCount: number +} + +/** The chain an org's tier names, or null when it was deleted since the rule was saved. */ +export const loadEscalationChain = (db: MapleDb, orgId: OrgId, chainId: EscalationChainId) => + db + .run( + PG.from(EscalationChains) + .select("id", "name", "stepsJson", "repeatCount") + .where(($) => [$.orgId.eq(orgId), $.id.eq(chainId)]) + .limit(1), + ) + .pipe( + Effect.map((rows) => + Arr.match(rows, { + onEmpty: (): EscalationChainSnapshot | null => null, + onNonEmpty: ([row]) => ({ + id: row.id, + name: row.name, + steps: row.stepsJson, + repeatCount: row.repeatCount, + }), + }), + ), + ) + +export interface StartEscalationRunInput { + readonly id: AlertEscalationRunId + readonly orgId: OrgId + readonly incidentId: AlertIncidentId + readonly chain: EscalationChainSnapshot + readonly severity: AlertSeverity + /** The incident's trigger notification; each step resends it to the step's targets. */ + readonly payload: Record + readonly nowMs: number +} + +/** + * Starts `chain` for an incident, superseding the run already live on it. The first step is due + * now, and its delivery key matches the incident's own trigger, so a destination both in the tier + * and in step 1 is notified once. + */ +export const startEscalationRun = (db: MapleDb, input: StartEscalationRunInput) => + Effect.gen(function* () { + yield* db.run( + PG.update(AlertEscalationRuns) + .set({ status: "superseded", endedAt: input.nowMs, updatedAt: input.nowMs }) + .where(($) => [ + $.orgId.eq(input.orgId), + $.incidentId.eq(input.incidentId), + $.status.in_(...LIVE_STATUSES), + ]), + ) + yield* db.run( + PG.insertInto(AlertEscalationRuns).values({ + id: input.id, + orgId: input.orgId, + incidentId: input.incidentId, + chainId: input.chain.id, + chainName: input.chain.name, + severity: input.severity, + stepsJson: input.chain.steps, + repeatCount: input.chain.repeatCount, + stepIndex: 0, + passIndex: 0, + nextStepAt: input.nowMs, + status: "active", + payloadJson: input.payload, + stepsSent: 0, + lastStepAt: null, + endedAt: null, + createdAt: input.nowMs, + updatedAt: input.nowMs, + }), + ) + }) + +/** + * Live runs with something to decide: due, paused on an ack that may be gone, or on an incident + * that is no longer open. Across orgs: the scheduler is the system actor. + */ +export const selectEscalationWork = (db: MapleDb, nowMs: number) => + db.run( + PG.from(AlertEscalationRuns, "r") + .innerJoin(AlertIncidents, "i", (r, i) => r.incidentId.eq(i.id).and(r.orgId.eq(i.orgId))) + .select(($) => ({ + id: $.id, + orgId: $.orgId, + incidentId: $.incidentId, + chainId: $.chainId, + chainName: $.chainName, + status: $.status, + stepsJson: $.stepsJson, + repeatCount: $.repeatCount, + stepIndex: $.stepIndex, + passIndex: $.passIndex, + nextStepAt: $.nextStepAt, + payloadJson: $.payloadJson, + stepsSent: $.stepsSent, + ruleId: $.i.ruleId, + groupKey: $.i.groupKey, + severity: $.i.severity, + incidentStatus: $.i.status, + acknowledgedAt: $.i.acknowledgedAt, + })) + .where(($) => [ + $.status.in_(...LIVE_STATUSES), + PG.or($.nextStepAt.lte(nowMs), $.status.eq("acknowledged"), $.i.status.neq("open")), + ]) + .orderBy(["nextStepAt", "asc"]) + .limit(ESCALATION_RUNS_PER_TICK), + ) + +export type EscalationWorkRow = Effect.Success>[number] + +export const planEscalationWork = (row: EscalationWorkRow, silenced: boolean, nowMs: number) => + planEscalationAdvance( + { + status: row.status, + steps: row.stepsJson, + repeatCount: row.repeatCount, + stepIndex: row.stepIndex, + passIndex: row.passIndex, + nextStepAt: row.nextStepAt, + }, + { open: row.incidentStatus === "open", acknowledged: row.acknowledgedAt !== null, silenced }, + nowMs, + ) + +/** The enabled destinations among `ids` in `orgId`; a deleted or disabled one is skipped. */ +export const enabledDestinationIds = (db: MapleDb, orgId: OrgId, ids: ReadonlyArray) => + ids.length === 0 + ? Effect.succeed(new Set()) + : db + .run( + PG.from(AlertDestinations) + .select("id") + .where(($) => [$.orgId.eq(orgId), $.id.in_(...ids), $.enabled.eq(true)]), + ) + .pipe(Effect.map((rows) => new Set(Arr.map(rows, (row) => row.id)))) + +export type EscalationApplyOutcome = "sent" | "ended" | "paused" | "resumed" | "deferred" | "lost" | "idle" + +type RunUpdate = Partial> +type EscalationRunInsert = PG.InsertRowOf + +/** Moves the run only from the position it was read at, so of two concurrent writers one wins. */ +const atPosition = (row: EscalationWorkRow, set: RunUpdate) => + PG.update(AlertEscalationRuns) + .set(set) + .where(($) => [ + $.id.eq(row.id), + $.status.eq(row.status), + $.stepIndex.eq(row.stepIndex), + $.passIndex.eq(row.passIndex), + $.nextStepAt.eq(row.nextStepAt), + ]) + .returning("id") + +export interface ApplyEscalationPlanInput { + readonly row: EscalationWorkRow + readonly plan: EscalationAdvancePlan + readonly nowMs: number + /** Of the step's destinations, those enabled; only needed for a `send`. */ + readonly enabled: ReadonlySet + readonly makeDeliveryId: () => string +} + +/** Applies one plan; run inside a transaction so a step's position and deliveries commit together. */ +export const applyEscalationPlan = (db: MapleDb, input: ApplyEscalationPlanInput) => + Effect.gen(function* () { + const { row, plan, nowMs } = input + const write = (set: RunUpdate) => + db.run(atPosition(row, { ...set, updatedAt: nowMs })).pipe(Effect.map((rows) => rows.length > 0)) + switch (plan.kind) { + case "wait": + return "idle" satisfies EscalationApplyOutcome + case "end": + return (yield* write({ status: plan.status, endedAt: nowMs })) ? "ended" : "lost" + case "pause": + return (yield* write({ status: "acknowledged" })) ? "paused" : "lost" + case "resume": + return (yield* write({ status: "active" })) ? "resumed" : "lost" + case "defer": + return (yield* write({ status: "active", nextStepAt: plan.nextStepAt })) ? "deferred" : "lost" + case "send": { + const moved = yield* write( + plan.next.kind === "exhausted" + ? { + status: "exhausted", + endedAt: nowMs, + stepsSent: row.stepsSent + 1, + lastStepAt: nowMs, + } + : { + status: "active", + stepIndex: plan.next.stepIndex, + passIndex: plan.next.passIndex, + nextStepAt: plan.next.nextStepAt, + stepsSent: row.stepsSent + 1, + lastStepAt: nowMs, + }, + ) + if (!moved) return "lost" satisfies EscalationApplyOutcome + const payload = { + ...row.payloadJson, + sentAt: new Date(nowMs).toISOString(), + escalation: { + chainId: row.chainId, + chainName: row.chainName, + step: plan.stepIndex + 1, + stepCount: row.stepsJson.length, + pass: plan.passIndex + 1, + passCount: row.repeatCount + 1, + }, + } + const destinationIds = Arr.dedupe( + Arr.filter( + Arr.map(plan.step.targets, (target) => target.destinationId), + (id) => input.enabled.has(id), + ), + ) + yield* Effect.forEach( + destinationIds, + (destinationId) => + db.run( + PG.insertInto(AlertDeliveryEvents) + .values( + queuedDeliveryEventRow({ + id: input.makeDeliveryId(), + orgId: row.orgId, + incidentId: row.incidentId, + ruleId: row.ruleId, + destinationId, + eventType: "trigger", + payload, + scheduledAt: nowMs, + // Keyed by the step's due time: the step a tier trigger shares a + // destination with dedupes against it, and no two steps share one. + deliveryKey: makeAlertDeliveryKey( + row.incidentId, + destinationId, + "trigger", + row.nextStepAt, + ), + attemptNumber: 1, + }), + ) + .onConflictDoNothing(), + ), + { discard: true }, + ) + return "sent" satisfies EscalationApplyOutcome + } + } + }) diff --git a/packages/backend/src/services/alerts/alert-thresholds.ts b/packages/backend/src/services/alerts/alert-thresholds.ts index c2b6c430a5..e635d5c788 100644 --- a/packages/backend/src/services/alerts/alert-thresholds.ts +++ b/packages/backend/src/services/alerts/alert-thresholds.ts @@ -1,6 +1,7 @@ import { compareAlertThreshold, maxSeverity, tierFor } from "@maple/alerting-core" import { ALERT_SEVERITY_RANK, + alertSeverityOrder, isRangeComparator, MAX_ALERT_BREACH_WINDOW, sortAlertThresholds, @@ -9,8 +10,9 @@ import { type AlertSeverity, type AlertThreshold, type AlertThresholds, + type EscalationChainId, } from "@maple/domain/http" -import { Array as Arr, Schema } from "effect" +import { Array as Arr, Option, Schema } from "effect" const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)) @@ -29,6 +31,27 @@ const destinationsUpTo = ( ), ) +/** + * The chain an incident at `severity` starts: its tier's, else the closest lower tier's, as a + * critical incident also reaches the warning tier's destinations. Null when none names one. + */ +export const escalationChainFor = ( + thresholds: ReadonlyArray, + severity: AlertSeverity, +): EscalationChainId | null => { + const candidates = Arr.filter( + thresholds, + (tier) => + ALERT_SEVERITY_RANK[tier.severity] <= ALERT_SEVERITY_RANK[severity] && + tier.escalationChainId !== undefined && + tier.escalationChainId !== null, + ) + return Option.match(Arr.last(Arr.sort(candidates, alertSeverityOrder)), { + onNone: () => null, + onSome: (tier) => tier.escalationChainId ?? null, + }) +} + interface TieredRuleFields { readonly thresholds: AlertThresholds readonly severity: AlertSeverity diff --git a/packages/backend/src/services/org/OrganizationService.ts b/packages/backend/src/services/org/OrganizationService.ts index b430f8d765..ffc51fd995 100644 --- a/packages/backend/src/services/org/OrganizationService.ts +++ b/packages/backend/src/services/org/OrganizationService.ts @@ -30,6 +30,7 @@ import { Actors, AgentFeedback, AlertDeliveryEvents, + AlertEscalationRuns, AlertIncidentEvents, AlertDestinations, AlertIncidents, @@ -38,6 +39,7 @@ import { AlertRules, ApiKeys, CancellationReviews, + EscalationChains, ChatIdentities, ChatWorkspaces, CliDeviceAuthorizations, @@ -142,7 +144,9 @@ const ORG_SCOPED_TABLES: ReadonlyArray = [ AlertDeliveryEvents, // Before its incident: an event row references it. AlertIncidentEvents, + AlertEscalationRuns, AlertIncidents, + EscalationChains, AlertRuleStates, AlertRules, // Before its destination: a thread row references it. diff --git a/packages/db/drizzle/20261106113702_escalation_chains/migration.sql b/packages/db/drizzle/20261106113702_escalation_chains/migration.sql new file mode 100644 index 0000000000..b5e0e2e2e4 --- /dev/null +++ b/packages/db/drizzle/20261106113702_escalation_chains/migration.sql @@ -0,0 +1,52 @@ +-- metadata +CREATE TABLE IF NOT EXISTS "alert_escalation_runs" ( + "id" text NOT NULL, + "org_id" text NOT NULL, + "incident_id" text NOT NULL, + "chain_id" text NOT NULL, + "chain_name" text NOT NULL, + "severity" text NOT NULL, + "steps_json" jsonb NOT NULL, + "repeat_count" integer NOT NULL, + "step_index" integer NOT NULL DEFAULT 0, + "pass_index" integer NOT NULL DEFAULT 0, + "next_step_at" timestamp with time zone NOT NULL, + "status" text NOT NULL, + "payload_json" jsonb NOT NULL, + "steps_sent" integer NOT NULL DEFAULT 0, + "last_step_at" timestamp with time zone, + "ended_at" timestamp with time zone, + "created_at" timestamp with time zone NOT NULL, + "updated_at" timestamp with time zone NOT NULL, + CONSTRAINT "alert_escalation_runs_pkey" PRIMARY KEY ("id") +); +--> statement-breakpoint +-- metadata +CREATE TABLE IF NOT EXISTS "escalation_chains" ( + "id" text NOT NULL, + "org_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "steps_json" jsonb NOT NULL, + "repeat_count" integer NOT NULL DEFAULT 0, + "created_at" timestamp with time zone NOT NULL, + "updated_at" timestamp with time zone NOT NULL, + "created_by" text NOT NULL, + "updated_by" text NOT NULL, + CONSTRAINT "escalation_chains_pkey" PRIMARY KEY ("id") +); +--> statement-breakpoint +-- metadata +CREATE INDEX IF NOT EXISTS "alert_escalation_runs_live_idx" ON "alert_escalation_runs" USING btree ("status", "next_step_at") WHERE "status" in ('active', 'acknowledged'); +--> statement-breakpoint +-- metadata +CREATE UNIQUE INDEX IF NOT EXISTS "alert_escalation_runs_live_incident_idx" ON "alert_escalation_runs" USING btree ("incident_id") WHERE "status" IN ('active', 'acknowledged'); +--> statement-breakpoint +-- metadata +CREATE INDEX IF NOT EXISTS "alert_escalation_runs_org_incident_idx" ON "alert_escalation_runs" USING btree ("org_id", "incident_id"); +--> statement-breakpoint +-- metadata +CREATE INDEX IF NOT EXISTS "escalation_chains_org_idx" ON "escalation_chains" USING btree ("org_id"); +--> statement-breakpoint +-- metadata +ALTER TABLE "alert_escalation_runs" ADD CONSTRAINT "alert_escalation_runs_incident_id_alert_incidents_id_fk" FOREIGN KEY ("incident_id") REFERENCES "alert_incidents" ("id") ON DELETE CASCADE; diff --git a/packages/db/drizzle/20261106113702_escalation_chains/snapshot.json b/packages/db/drizzle/20261106113702_escalation_chains/snapshot.json new file mode 100644 index 0000000000..653325bf59 --- /dev/null +++ b/packages/db/drizzle/20261106113702_escalation_chains/snapshot.json @@ -0,0 +1,19340 @@ +{ + "version": "1", + "dialect": "postgres", + "id": "82f424c799908655a6841b74922baf95292238868d3334b18eecdfb160b1c73b", + "prevIds": [ + "735b86449ab62a2a8b8163dbb4278bca8e991f0dfafc0f3b7c2418ea6b2446b9" + ], + "entities": [ + { + "kind": "table", + "name": "actors", + "primaryKey": { + "name": "actors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "agent_feedback", + "primaryKey": { + "name": "agent_feedback_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "ai_triage_settings", + "primaryKey": { + "name": "ai_triage_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "alert_delivery_events", + "primaryKey": { + "name": "alert_delivery_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_destinations", + "primaryKey": { + "name": "alert_destinations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_escalation_runs", + "primaryKey": { + "name": "alert_escalation_runs_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_incident_events", + "primaryKey": { + "name": "alert_incident_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_incidents", + "primaryKey": { + "name": "alert_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_linear_issues", + "primaryKey": { + "name": "alert_linear_issues_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "alert_rule_claims", + "primaryKey": { + "name": "alert_rule_claims_pkey", + "columns": [ + "rule_id" + ] + } + }, + { + "kind": "table", + "name": "alert_rule_states", + "primaryKey": { + "name": "alert_rule_states_org_id_rule_id_group_key_pk", + "columns": [ + "org_id", + "rule_id", + "group_key" + ] + } + }, + { + "kind": "table", + "name": "alert_rules", + "primaryKey": { + "name": "alert_rules_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "anomaly_detector_settings", + "primaryKey": { + "name": "anomaly_detector_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "anomaly_detector_states", + "primaryKey": { + "name": "anomaly_detector_states_org_id_detector_key_pk", + "columns": [ + "org_id", + "detector_key" + ] + } + }, + { + "kind": "table", + "name": "anomaly_incidents", + "primaryKey": { + "name": "anomaly_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "api_keys", + "primaryKey": { + "name": "api_keys_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "aws_connections", + "primaryKey": { + "name": "aws_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cancellation_reviews", + "primaryKey": { + "name": "cancellation_reviews_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "chat_identities", + "primaryKey": { + "name": "chat_identities_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "chat_workspaces", + "primaryKey": { + "name": "chat_workspaces_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cli_device_authorizations", + "primaryKey": { + "name": "cli_device_authorizations_pkey", + "columns": [ + "device_code_hash" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_analytics_state", + "primaryKey": { + "name": "cloudflare_analytics_state_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_hyperdrive_configs", + "primaryKey": { + "name": "cloudflare_hyperdrive_configs_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_logpush_connectors", + "primaryKey": { + "name": "cloudflare_logpush_connectors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "cloudflare_zone_settings", + "primaryKey": { + "name": "cloudflare_zone_settings_pkey", + "columns": [ + "org_id", + "account_id" + ] + } + }, + { + "kind": "table", + "name": "dashboard_shares", + "primaryKey": { + "name": "dashboard_shares_org_id_id_pk", + "columns": [ + "org_id", + "id" + ] + } + }, + { + "kind": "table", + "name": "dashboard_versions", + "primaryKey": { + "name": "dashboard_versions_org_id_id_pk", + "columns": [ + "org_id", + "id" + ] + } + }, + { + "kind": "table", + "name": "dashboards", + "primaryKey": { + "name": "dashboards_org_id_id_pk", + "columns": [ + "org_id", + "id" + ] + } + }, + { + "kind": "table", + "name": "dependency_findings", + "primaryKey": { + "name": "dependency_findings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "dependency_scan_matches", + "primaryKey": { + "name": "dependency_scan_matches_pkey", + "columns": [ + "scan_id", + "lockfile_path", + "ecosystem", + "package_name", + "version", + "advisory_id" + ] + } + }, + { + "kind": "table", + "name": "dependency_scans", + "primaryKey": { + "name": "dependency_scans_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "device_credentials", + "primaryKey": { + "name": "device_credentials_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "device_proof_replays", + "primaryKey": { + "name": "device_proof_replays_pkey", + "columns": [ + "key" + ] + } + }, + { + "kind": "table", + "name": "digest_subscriptions", + "primaryKey": { + "name": "digest_subscriptions_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_fingerprint_candidates", + "primaryKey": { + "name": "error_fingerprint_candidates_org_id_fingerprint_hash_pk", + "columns": [ + "org_id", + "fingerprint_hash" + ] + } + }, + { + "kind": "table", + "name": "error_incidents", + "primaryKey": { + "name": "error_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_events", + "primaryKey": { + "name": "error_issue_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_fingerprints", + "primaryKey": { + "name": "error_issue_fingerprints_org_id_fingerprint_hash_pk", + "columns": [ + "org_id", + "fingerprint_hash" + ] + } + }, + { + "kind": "table", + "name": "error_issue_pull_requests", + "primaryKey": { + "name": "error_issue_pull_requests_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_states", + "primaryKey": { + "name": "error_issue_states_org_id_issue_id_pk", + "columns": [ + "org_id", + "issue_id" + ] + } + }, + { + "kind": "table", + "name": "error_issue_verifications", + "primaryKey": { + "name": "error_issue_verifications_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_issues", + "primaryKey": { + "name": "error_issues_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_notification_deliveries", + "primaryKey": { + "name": "error_notification_deliveries_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "error_notification_policies", + "primaryKey": { + "name": "error_notification_policies_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "error_tick_states", + "primaryKey": { + "name": "error_tick_states_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "escalation_chains", + "primaryKey": { + "name": "escalation_chains_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "gcp_connectors", + "primaryKey": { + "name": "gcp_connectors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "gcp_resources", + "primaryKey": { + "name": "gcp_resources_pkey", + "columns": [ + "connector_id", + "name" + ] + } + }, + { + "kind": "table", + "name": "investigations", + "primaryKey": { + "name": "investigations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "issue_escalation_policies", + "primaryKey": { + "name": "issue_escalation_policies_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "issue_escalations", + "primaryKey": { + "name": "issue_escalations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "live_activities", + "primaryKey": { + "name": "live_activities_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "mcp_oauth_authorizations", + "primaryKey": { + "name": "mcp_oauth_authorizations_pkey", + "columns": [ + "request_id_hash" + ] + } + }, + { + "kind": "table", + "name": "mcp_oauth_clients", + "primaryKey": { + "name": "mcp_oauth_clients_pkey", + "columns": [ + "client_id" + ] + } + }, + { + "kind": "table", + "name": "mcp_oauth_refresh_tokens", + "primaryKey": { + "name": "mcp_oauth_refresh_tokens_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "mobile_devices", + "primaryKey": { + "name": "mobile_devices_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "oauth_auth_states", + "primaryKey": { + "name": "oauth_auth_states_pkey", + "columns": [ + "state" + ] + } + }, + { + "kind": "table", + "name": "oauth_connections", + "primaryKey": { + "name": "oauth_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_billing_links", + "primaryKey": { + "name": "org_billing_links_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_clickhouse_schema_apply_runs", + "primaryKey": { + "name": "org_clickhouse_schema_apply_runs_org_id_pk", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_clickhouse_settings", + "primaryKey": { + "name": "org_clickhouse_settings_org_id_pk", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_attribute_mappings", + "primaryKey": { + "name": "org_ingest_attribute_mappings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_keys", + "primaryKey": { + "name": "org_ingest_keys_org_id_pk", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_rule_settings", + "primaryKey": { + "name": "org_ingest_rule_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_rule_stats", + "primaryKey": { + "name": "org_ingest_rule_stats_pkey", + "columns": [ + "org_id", + "rule_id", + "bucket", + "signal" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_rules", + "primaryKey": { + "name": "org_ingest_rules_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_ingest_sampling_policies", + "primaryKey": { + "name": "org_ingest_sampling_policies_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_onboarding_state", + "primaryKey": { + "name": "org_onboarding_state_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_openrouter_keys", + "primaryKey": { + "name": "org_openrouter_keys_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "org_recommendation_issues", + "primaryKey": { + "name": "org_recommendation_issues_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "org_support_channels", + "primaryKey": { + "name": "org_support_channels_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_connections", + "primaryKey": { + "name": "planetscale_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_databases", + "primaryKey": { + "name": "planetscale_databases_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_events", + "primaryKey": { + "name": "planetscale_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_issue_receipts", + "primaryKey": { + "name": "planetscale_issue_receipts_org_id_event_id_pk", + "columns": [ + "org_id", + "event_id" + ] + } + }, + { + "kind": "table", + "name": "planetscale_poll_state", + "primaryKey": { + "name": "planetscale_poll_state_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_edits", + "primaryKey": { + "name": "pr_review_edits_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_finding_embeddings", + "primaryKey": { + "name": "pr_review_finding_embeddings_pkey", + "columns": [ + "finding_id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_findings", + "primaryKey": { + "name": "pr_review_findings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_merge_steps", + "primaryKey": { + "name": "pr_review_merge_steps_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_replies", + "primaryKey": { + "name": "pr_review_replies_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "pr_review_settings", + "primaryKey": { + "name": "pr_review_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "pr_reviews", + "primaryKey": { + "name": "pr_reviews_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "railway_connections", + "primaryKey": { + "name": "railway_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "railway_environments", + "primaryKey": { + "name": "railway_environments_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "scrape_target_checks", + "primaryKey": { + "name": "scrape_target_checks_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "scrape_targets", + "primaryKey": { + "name": "scrape_targets_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_events", + "primaryKey": { + "name": "security_audit_events_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_findings", + "primaryKey": { + "name": "security_audit_findings_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_org_reservations", + "primaryKey": { + "name": "security_audit_org_reservations_pkey", + "columns": [ + "run_id", + "agent_id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_org_settings", + "primaryKey": { + "name": "security_audit_org_settings_pkey", + "columns": [ + "org_id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_run_findings", + "primaryKey": { + "name": "security_audit_run_findings_pkey", + "columns": [ + "run_id", + "fingerprint" + ] + } + }, + { + "kind": "table", + "name": "security_audit_runs", + "primaryKey": { + "name": "security_audit_runs_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_targets", + "primaryKey": { + "name": "security_audit_targets_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "security_audit_units", + "primaryKey": { + "name": "security_audit_units_pkey", + "columns": [ + "run_id", + "coverage_id" + ] + } + }, + { + "kind": "table", + "name": "sources", + "primaryKey": { + "name": "sources_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_cleanups", + "primaryKey": { + "name": "status_page_cleanups_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_components", + "primaryKey": { + "name": "status_page_components_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_incident_components", + "primaryKey": { + "name": "status_page_incident_components_pk", + "columns": [ + "incident_id", + "component_id" + ] + } + }, + { + "kind": "table", + "name": "status_page_incident_updates", + "primaryKey": { + "name": "status_page_incident_updates_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_page_incidents", + "primaryKey": { + "name": "status_page_incidents_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "status_pages", + "primaryKey": { + "name": "status_pages_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "supabase_connections", + "primaryKey": { + "name": "supabase_connections_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "supabase_project_insights", + "primaryKey": { + "name": "supabase_project_insights_pkey", + "columns": [ + "project_id" + ] + } + }, + { + "kind": "table", + "name": "supabase_projects", + "primaryKey": { + "name": "supabase_projects_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "uptime_monitor_daily", + "primaryKey": { + "name": "uptime_monitor_daily_pk", + "columns": [ + "monitor_id", + "day" + ] + } + }, + { + "kind": "table", + "name": "uptime_monitor_states", + "primaryKey": { + "name": "uptime_monitor_states_pkey", + "columns": [ + "monitor_id" + ] + } + }, + { + "kind": "table", + "name": "uptime_monitors", + "primaryKey": { + "name": "uptime_monitors_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_commits", + "primaryKey": { + "name": "vcs_commits_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_installations", + "primaryKey": { + "name": "vcs_installations_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_repositories", + "primaryKey": { + "name": "vcs_repositories_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "table", + "name": "vcs_repository_branches", + "primaryKey": { + "name": "vcs_repository_branches_pkey", + "columns": [ + "id" + ] + } + }, + { + "kind": "column", + "table": "actors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "type", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "user_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "agent_name", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "model", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "capabilities_json", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "created_by", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "actors", + "name": "last_active_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "impact", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "summary", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "reason", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "details", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "related_to", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_type", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_name", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_model", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "agent_version", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "source", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "agent_feedback", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "max_runs_per_day", + "position": 2, + "type": "integer", + "notNull": true, + "default": "20", + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "max_passes_per_day", + "position": 3, + "type": "integer", + "notNull": true, + "default": "90", + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "updated_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "ai_triage_settings", + "name": "updated_by", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "incident_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "rule_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "destination_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "delivery_key", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "event_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "attempt_number", + "position": 7, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "status", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "scheduled_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "claimed_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "claim_expires_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "claimed_by", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "attempted_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "provider_message", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "provider_reference", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "response_code", + "position": 16, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "error_message", + "position": 17, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "payload_json", + "position": 18, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "created_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_delivery_events", + "name": "updated_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "enabled", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "config_json", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "secret_ciphertext", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "secret_iv", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "secret_tag", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "last_tested_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "last_test_error", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "consecutive_failures", + "position": 11, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "last_failure_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "disabled_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "disabled_reason", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "created_by", + "position": 17, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_destinations", + "name": "updated_by", + "position": 18, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "incident_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "chain_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "chain_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "severity", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "steps_json", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "repeat_count", + "position": 7, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "step_index", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "pass_index", + "position": 9, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "next_step_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "status", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "payload_json", + "position": 12, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "steps_sent", + "position": 13, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "last_step_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "ended_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "created_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_escalation_runs", + "name": "updated_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "incident_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "actor_user_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "actor_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "source", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incident_events", + "name": "created_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "rule_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "incident_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "rule_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "group_key", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "signal_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "severity", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "peak_severity", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "comparator", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "threshold", + "position": 11, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "threshold_upper", + "position": 12, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "first_triggered_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_triggered_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "resolved_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_observed_value", + "position": 16, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_sample_count", + "position": 17, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_evaluated_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "dedupe_key", + "position": 19, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_delivered_event_type", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "last_notified_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "hold_reason", + "position": 22, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "held_since", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_by_user_id", + "position": 25, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_by_name", + "position": 26, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "acknowledged_via", + "position": 27, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "error_issue_id", + "position": 28, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "created_at", + "position": 29, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_incidents", + "name": "updated_at", + "position": 30, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "destination_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "thread_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_team_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_issue_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_identifier", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "linear_url", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "incident_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_linear_issues", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "rule_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "last_scheduled_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_claims", + "name": "consecutive_limit_failures", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "rule_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "group_key", + "position": 2, + "type": "text", + "notNull": true, + "default": "'__total__'", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "consecutive_breaches", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "consecutive_healthy", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "history_json", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_status", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_value", + "position": 7, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_sample_count", + "position": 8, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_evaluated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_error", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "last_error_kind", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rule_states", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "notes", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "notification_template_json", + "position": 4, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "enabled", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "severity", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "service_names_json", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "exclude_service_names_json", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "environments_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "tags_json", + "position": 10, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "signal_type", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "comparator", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "threshold", + "position": 13, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "threshold_upper", + "position": 14, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "thresholds_json", + "position": 15, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "window_minutes", + "position": 16, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "evaluation_interval_minutes", + "position": 17, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "slowed_interval_minutes", + "position": 18, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "slowed_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "minimum_sample_count", + "position": 20, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "consecutive_breaches_required", + "position": 21, + "type": "integer", + "notNull": true, + "default": "2", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "breaches_out_of", + "position": 22, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "consecutive_healthy_required", + "position": 23, + "type": "integer", + "notNull": true, + "default": "2", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "renotify_interval_minutes", + "position": 24, + "type": "integer", + "notNull": true, + "default": "30", + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "apdex_threshold_ms", + "position": 25, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "query_builder_draft_json", + "position": 26, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "raw_query_sql", + "position": 27, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "group_by", + "position": 28, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "destination_ids_json", + "position": 29, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "query_spec_json", + "position": 30, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "reducer", + "position": 31, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "sample_count_strategy", + "position": 32, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "no_data_behavior", + "position": 33, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "last_scheduled_at", + "position": 34, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "created_at", + "position": 35, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "updated_at", + "position": 36, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "created_by", + "position": 37, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "alert_rules", + "name": "updated_by", + "position": 38, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "sensitivity", + "position": 2, + "type": "text", + "notNull": true, + "default": "'normal'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "muted_signals_json", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "last_tick_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "updated_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_settings", + "name": "updated_by", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "detector_key", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "signal_type", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "service_name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "deployment_env", + "position": 4, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "fingerprint_hash", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "consecutive_breaches", + "position": 6, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "consecutive_healthy", + "position": 7, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_status", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_value", + "position": 9, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "baseline_median", + "position": 10, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_sample_count", + "position": 11, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_evaluated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "open_incident_id", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_resolved_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "last_incident_id", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_detector_states", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "detector_key", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "signal_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "service_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "deployment_env", + "position": 5, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "fingerprint_hash", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "error_issue_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "status", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "severity", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "opened_value", + "position": 10, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "baseline_median", + "position": 11, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "baseline_sigma", + "position": 12, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "threshold_value", + "position": 13, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_observed_value", + "position": 14, + "type": "double precision", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_sample_count", + "position": 15, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "first_triggered_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_triggered_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "resolved_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "resolve_reason", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "triage_status", + "position": 20, + "type": "text", + "notNull": true, + "default": "'none'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "dedupe_key", + "position": 21, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "fingerprints_json", + "position": 22, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "reopen_count", + "position": 23, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "last_reopened_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "created_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "anomaly_incidents", + "name": "updated_at", + "position": 26, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "description", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "key_hash", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "key_prefix", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "revoked", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "revoked_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "last_used_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "expires_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "metadata_json", + "position": 10, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "scopes", + "position": 11, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "kind", + "position": 12, + "type": "text", + "notNull": true, + "default": "'standard'", + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "created_by", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "api_keys", + "name": "created_by_email", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "account_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "modules", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "granted_modules", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "template_version", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "verified_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "last_checked_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "last_check_error", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "check_lease_until", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "metric_stream", + "position": 11, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "metric_stream_changed_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_ciphertext", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_iv", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_tag", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_secret_hash", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_last_received_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "stream_last_error", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "created_by", + "position": 19, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "created_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "aws_connections", + "name": "updated_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "plan_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "subscription_started_at", + "position": 3, + "type": "bigint", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "canceled_at", + "position": 4, + "type": "bigint", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "snapshot_json", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "rule_reason", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "model_read", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "posted_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cancellation_reviews", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "connector", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "external_user_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "user_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "display_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_identities", + "name": "created_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "connector", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "external_workspace_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "settings", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "credentials_ciphertext", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "credentials_iv", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "credentials_tag", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "chat_workspaces", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "device_code_hash", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "user_code_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "device_name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_org_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_user_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_roles", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_user_email", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "api_key_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "token_ciphertext", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "token_iv", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "token_tag", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "approved_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "denied_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cli_device_authorizations", + "name": "expires_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "account_id", + "position": 2, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "dataset", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "zone_id", + "position": 4, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "zone_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "enabled", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "watermark_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "backfill_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "settings_json", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "settings_fetched_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "quantiles_available", + "position": 11, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "discovered_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "live_scripts_json", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "grant_call_cap", + "position": 14, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "last_success_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "last_error", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "last_error_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "lease_until", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "created_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_analytics_state", + "name": "updated_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "account_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "config_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_host", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_port", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_scheme", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_database", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "origin_user", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "deleted_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "created_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_hyperdrive_configs", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "zone_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "service_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "dataset", + "position": 6, + "type": "text", + "notNull": true, + "default": "'http_requests'", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_ciphertext", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_iv", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_tag", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_hash", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "enabled", + "position": 11, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "last_received_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "last_error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "secret_rotated_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "created_by", + "position": 17, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_logpush_connectors", + "name": "updated_by", + "position": 18, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "account_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "watch_new_zones", + "position": 2, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "zone_overrides", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "cloudflare_zone_settings", + "name": "updated_by", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "dashboard_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "widget_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "mode", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_hash", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_ciphertext", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_iv", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_tag", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "token_suffix", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "created_by", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "updated_by", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_shares", + "name": "revoked_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "dashboard_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "version_number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "snapshot_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "change_kind", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "change_summary", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "source_version_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboard_versions", + "name": "created_by", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "payload_json", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "created_by", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "updated_by", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dashboards", + "name": "version", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "lockfile_path", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "ecosystem", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "package_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "advisory_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "aliases", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "severity", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "summary", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "versions", + "position": 10, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "fixed_in", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "relation", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status", + "position": 13, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "first_seen_scan_id", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "last_seen_scan_id", + "position": 15, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "first_seen_sha", + "position": 16, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "last_seen_sha", + "position": 17, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "fixed_scan_id", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "fixed_sha", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status_reason", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status_changed_by", + "position": 21, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "status_changed_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "created_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_findings", + "name": "updated_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "scan_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "lockfile_path", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "ecosystem", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "package_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "version", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "relation", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "advisory_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "aliases", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "severity", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "summary", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scan_matches", + "name": "fixed_in", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "commit_sha", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "ref", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "trigger", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "default_branch_head", + "position": 6, + "type": "boolean", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "status", + "position": 7, + "type": "text", + "notNull": true, + "default": "'running'", + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "lockfiles", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "package_count", + "position": 9, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "vulnerable_count", + "position": 10, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "error", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "started_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "dependency_scans", + "name": "finished_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "installation_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "public_jwk", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "jkt", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "roles", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "scopes", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "attested", + "position": 8, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "attestation", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "last_used_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "expires_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "hard_expires_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_credentials", + "name": "revoked_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_proof_replays", + "name": "key", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "device_proof_replays", + "name": "expires_at", + "position": 1, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "email", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "enabled", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "opted_out_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "day_of_week", + "position": 6, + "type": "integer", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "timezone", + "position": 7, + "type": "text", + "notNull": true, + "default": "'UTC'", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "send_hour", + "position": 8, + "type": "integer", + "notNull": true, + "default": "9", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "namespaces_json", + "position": 9, + "type": "text", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "environments_json", + "position": 10, + "type": "text", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "last_sent_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "last_attempted_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_enabled", + "position": 13, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_opted_out_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_last_sent_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "web_analytics_last_attempted_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "created_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "digest_subscriptions", + "name": "updated_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "fingerprint_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "service_name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "exception_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "exception_message", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "error_label", + "position": 5, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "top_frame", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "service_versions_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "occurrence_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "first_seen_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "last_seen_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_fingerprint_candidates", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "status", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "reason", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "first_triggered_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "last_triggered_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "resolved_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "occurrence_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_incidents", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "actor_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "type", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "from_state", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "to_state", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "payload_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_events", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "fingerprint_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "source", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "origin_issue_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_fingerprints", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "provider", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "external_repo_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "repo_full_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "number", + "position": 6, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "url", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "title", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "author_login", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "state", + "position": 10, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "merged_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "merge_commit_sha", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "link_source", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "linked_by_actor_id", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_pull_requests", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "issue_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "last_observed_occurrence_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "last_evaluated_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "open_incident_id", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_states", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "pull_request_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "status", + "position": 4, + "type": "text", + "notNull": true, + "default": "'waiting'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "merged_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "verify_after", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "baseline_versions_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "baseline_occurrence_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "baseline_rate_per_hour", + "position": 9, + "type": "double precision", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "investigation_id", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "verdict", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "verdict_note", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "post_merge_occurrence_count", + "position": 13, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "attempt", + "position": 14, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issue_verifications", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "kind", + "position": 2, + "type": "text", + "notNull": true, + "default": "'error'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "source_ref_json", + "position": 3, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "fingerprint_hash", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "fingerprint_version", + "position": 5, + "type": "integer", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "service_name", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "exception_type", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "exception_message", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "error_label", + "position": 9, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "top_frame", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "workflow_state", + "position": 11, + "type": "text", + "notNull": true, + "default": "'triage'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "priority", + "position": 12, + "type": "integer", + "notNull": true, + "default": "3", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "severity", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "severity_source", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "assigned_actor_id", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "lease_holder_actor_id", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "lease_expires_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "claimed_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "notes", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "first_seen_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "last_seen_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "occurrence_count", + "position": 22, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "resolved_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "resolved_by_actor_id", + "position": 24, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "last_resolved_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "last_regressed_at", + "position": 26, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "regression_count", + "position": 27, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "seen_versions_json", + "position": 28, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "resolved_versions_json", + "position": 29, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "snooze_until", + "position": 30, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "archived_at", + "position": 31, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "merged_into_issue_id", + "position": 32, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "created_at", + "position": 33, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_issues", + "name": "updated_at", + "position": 34, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "destination_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "delivery_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "payload_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "status", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "attempt_count", + "position": 6, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "scheduled_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "claimed_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "claim_expires_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "claimed_by", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "attempted_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "error_message", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_deliveries", + "name": "updated_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "destination_ids_json", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_first_seen", + "position": 3, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_regression", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_resolve", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_transition_in_review", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_transition_done", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "notify_on_claim", + "position": 8, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "min_occurrence_count", + "position": 9, + "type": "integer", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "severity", + "position": 10, + "type": "text", + "notNull": true, + "default": "'warning'", + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_notification_policies", + "name": "updated_by", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "processed_through", + "position": 1, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "bootstrap_completed", + "position": 2, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "claim_token", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "claim_expires_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "error_tick_states", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "description", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "steps_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "repeat_count", + "position": 5, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "created_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "updated_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "created_by", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "escalation_chains", + "name": "updated_by", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "scope_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "scope_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "project_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "logs_enabled", + "position": 6, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "metrics_enabled", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_ciphertext", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_iv", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_tag", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "secret_hash", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_received_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "applied_logs_enabled", + "position": 14, + "type": "boolean", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "applied_metrics_enabled", + "position": 15, + "type": "boolean", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "setup_reported_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "metrics_watermark_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_metrics_received_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_metrics_error", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "metrics_lease_until", + "position": 20, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "resources_synced_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "last_resources_error", + "position": 22, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "created_by", + "position": 23, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "created_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_connectors", + "name": "updated_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "connector_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "asset_type", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "project_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "location", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "display_name", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "state", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "labels", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "details", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "resource_created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "resource_updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "gcp_resources", + "name": "last_seen_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "status", + "position": 2, + "type": "text", + "notNull": true, + "default": "'investigating'", + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "seeded_by", + "position": 3, + "type": "text", + "notNull": true, + "default": "'user'", + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "subject_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "snapshot_json", + "position": 5, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "incident_kind", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "incident_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "issue_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "report_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "progress_json", + "position": 10, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "severity", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "confidence", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "model", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "input_tokens", + "position": 14, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "output_tokens", + "position": 15, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "error", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "started_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "autonomous_turns", + "position": 18, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "created_by", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "created_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "diagnosed_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "investigations", + "name": "updated_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "enabled", + "position": 1, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "rules_json", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "updated_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalation_policies", + "name": "updated_by", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "issue_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "severity", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "source", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "reason", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "run_id", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "investigation_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "payload_json", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "delivery_results_json", + "position": 9, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "status", + "position": 10, + "type": "text", + "notNull": true, + "default": "'queued'", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "attempts", + "position": 11, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "dedupe_key", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "issue_escalations", + "name": "processed_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "device_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "incident_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "activity_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "push_token", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "ended_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "ended_reason", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "live_activities", + "name": "updated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "request_id_hash", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "client_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "client_name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "redirect_uri", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "state", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "resource", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "scopes", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "code_challenge", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "authorization_code_hash", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_org_id", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_user_id", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_roles", + "position": 11, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_user_email", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "approved_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "denied_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "used_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "created_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_authorizations", + "name": "expires_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "client_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "client_name", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "redirect_uris", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "client_uri", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_clients", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "token_hash", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "family_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "client_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "resource", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "scopes", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "org_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "user_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "roles", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "user_email", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "access_key_id", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "replaced_by_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "revoked_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "expires_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mcp_oauth_refresh_tokens", + "name": "family_expires_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "user_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "platform", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "token", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "environment", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "bundle_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "app_version", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "live_activity_start_token", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "device_name", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "preferences", + "position": 10, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "disabled_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "disabled_reason", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "last_pushed_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "last_seen_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "mobile_devices", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "state", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "initiated_by_user_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "redirect_uri", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "return_to", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "code_verifier", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "github_installation_ids", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_auth_states", + "name": "expires_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "provider", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "external_user_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "external_user_email", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "external_account_name", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "granted_accounts_json", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "connected_by_user_id", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "scope", + "position": 9, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "access_token_ciphertext", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "access_token_iv", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "access_token_tag", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "refresh_token_ciphertext", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "refresh_token_iv", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "refresh_token_tag", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "expires_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "revoked_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "created_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "oauth_connections", + "name": "updated_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "billing_org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "linked_by", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_billing_links", + "name": "created_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "workflow_instance_id", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "status", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "phase", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "current_migration", + "position": 4, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "steps_total", + "position": 5, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "steps_done", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "applied_versions", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "skipped", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "error_message", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "started_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "finished_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "created_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_schema_apply_runs", + "name": "updated_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_url", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_user", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_password_ciphertext", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_password_iv", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_password_tag", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "ch_database", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "sync_status", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "last_sync_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "last_sync_error", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "schema_version", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "created_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "updated_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "created_by", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_clickhouse_settings", + "name": "updated_by", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "source_context", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "source_key", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "target_key", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "operation", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "enabled", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_attribute_mappings", + "name": "updated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "public_key", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "public_key_hash", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_ciphertext", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_iv", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_tag", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_key_hash", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "public_rotated_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "private_rotated_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "created_by", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_keys", + "name": "updated_by", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_settings", + "name": "hash_salt", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_settings", + "name": "created_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "rule_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "bucket", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "signal", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "matched", + "position": 4, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "dropped", + "position": 5, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "dropped_bytes", + "position": 6, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rule_stats", + "name": "redacted_values", + "position": 7, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "enabled", + "position": 3, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "position", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "spec_json", + "position": 5, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "created_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_rules", + "name": "updated_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "trace_sample_ratio", + "position": 1, + "type": "double precision", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "always_keep_error_spans", + "position": 2, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "always_keep_slow_spans_ms", + "position": 3, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "created_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_ingest_sampling_policies", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "user_id", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "email", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "role", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "demo_data_requested", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "onboarding_completed_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "checklist_dismissed_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "first_data_received_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "welcome_email_sent_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "connect_nudge_email_sent_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "stalled_email_sent_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "activation_email_sent_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "reward_claimed_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "reward_reserved_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_onboarding_state", + "name": "updated_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_ciphertext", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_iv", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_tag", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "key_hint", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "updated_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_openrouter_keys", + "name": "updated_by", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "number", + "position": 2, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "recommendation_key", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "kind", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "source_key", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "canonical_key", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "status", + "position": 7, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "usage_count", + "position": 8, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "opened_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": "now()", + "identity": null + }, + { + "kind": "column", + "table": "org_recommendation_issues", + "name": "resolved_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "slack_channel_id", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "slack_channel_name", + "position": 2, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "reservation_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "reserved_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "created_by_user_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "created_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "org_support_channels", + "name": "updated_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "ps_organization", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "connected_by_user_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "scrape_target_id", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "webhook_secret_ciphertext", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "webhook_secret_iv", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "webhook_secret_tag", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "detected_permissions_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "last_inventory_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "last_inventory_error", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "created_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_connections", + "name": "updated_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "database_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "kind", + "position": 4, + "type": "text", + "notNull": true, + "default": "'mysql'", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "state", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "region", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "plan", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "branches_json", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_id", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_secret_ciphertext", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_secret_iv", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_secret_tag", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "webhook_error", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "deleted_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_databases", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "database_id", + "position": 2, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "database_name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "branch_name", + "position": 4, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "category", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "event_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "state", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "external_id", + "position": 8, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "title", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "source", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "actor_login", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "url", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "payload_json", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "occurred_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_events", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_issue_receipts", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_issue_receipts", + "name": "event_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_issue_receipts", + "name": "processed_at", + "position": 2, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "dataset", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "database_id", + "position": 3, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "enabled", + "position": 4, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "watermark_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "last_success_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "last_error", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "last_error_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "lease_until", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "planetscale_poll_state", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "reply_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "seq", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "path", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "old_text", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "new_text", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_edits", + "name": "created_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "finding_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "model", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "embedding", + "position": 4, + "type": "real[]", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_finding_embeddings", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "review_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "handle", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "path", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "line", + "position": 7, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "category", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "severity", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "title", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "status", + "position": 11, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "comment_id", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "resolved_sha", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "reactions_up", + "position": 14, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "reactions_down", + "position": 15, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "created_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_findings", + "name": "updated_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "key", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "kind", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "title", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "source", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "path", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "done_by", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "done_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "reminded_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "first_review_id", + "position": 13, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "last_review_id", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_merge_steps", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "comment_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "surface", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "thread_root_id", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "author_login", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "command", + "position": 8, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "head_sha", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "status", + "position": 10, + "type": "text", + "notNull": true, + "default": "'queued'", + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "session_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "reply_url", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "commit_sha", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "error", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_replies", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "model", + "position": 1, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "defaults", + "position": 2, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "updated_at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_review_settings", + "name": "updated_by", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "repository_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "number", + "position": 3, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "head_sha", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "base_sha", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "url", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "title", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "author_login", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": "'queued'", + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "skip_reason", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "session_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "comment_attempt", + "position": 12, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "report_json", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "score", + "position": 14, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "check_run_url", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "comment_url", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "review_url", + "position": 17, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "publish_error", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "error", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "model", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "input_tokens", + "position": 21, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "output_tokens", + "position": 22, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "started_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "finished_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "merged_at", + "position": 25, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "telemetry_json", + "position": 26, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "merge_commit_sha", + "position": 27, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "post_merge_status", + "position": 28, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "post_merge_after", + "position": 29, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "post_merge_json", + "position": 30, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "created_at", + "position": 31, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "pr_reviews", + "name": "updated_at", + "position": 32, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "token_ciphertext", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "token_iv", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "token_tag", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "workspace_names", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "connected_by_user_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "auth_failed_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "discovered_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "lease_until", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "last_success_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "last_error", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "last_error_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_connections", + "name": "updated_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "connection_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "project_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "project_name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "environment_id", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "environment_name", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "services_json", + "position": 7, + "type": "text", + "notNull": true, + "default": "'{}'", + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "enabled", + "position": 8, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "watermark_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "last_success_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "last_error", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "last_error_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "railway_environments", + "name": "updated_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "id", + "position": 0, + "type": "integer", + "notNull": true, + "default": null, + "identity": "by default" + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "target_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "sub_target_key", + "position": 3, + "type": "text", + "notNull": true, + "default": "''", + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "checked_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "error", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "duration_ms", + "position": 6, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "samples_scraped", + "position": 7, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_target_checks", + "name": "samples_post_relabel", + "position": 8, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "service_name", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "url", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "target_type", + "position": 6, + "type": "text", + "notNull": true, + "default": "'prometheus'", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "discovery_config_json", + "position": 7, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "scrape_interval_seconds", + "position": 8, + "type": "integer", + "notNull": true, + "default": "15", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "labels_json", + "position": 9, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_type", + "position": 10, + "type": "text", + "notNull": true, + "default": "'none'", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "managed_by", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_credentials_ciphertext", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_credentials_iv", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "auth_credentials_tag", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "enabled", + "position": 15, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "last_scrape_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "last_scrape_error", + "position": 17, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "created_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "scrape_targets", + "name": "updated_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "run_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "at", + "position": 3, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "kind", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "role", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "agent_id", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "outcome", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "detail", + "position": 8, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "input_tokens", + "position": 9, + "type": "bigint", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_events", + "name": "output_tokens", + "position": 10, + "type": "bigint", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "target_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "fingerprint", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "verdict", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "overall_severity", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "estimated_severity", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "record", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status", + "position": 8, + "type": "text", + "notNull": true, + "default": "'open'", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "first_seen_run_id", + "position": 9, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "last_seen_run_id", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "first_seen_ref", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "last_seen_ref", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "fixed_run_id", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status_reason", + "position": 14, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status_changed_by", + "position": 15, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "status_changed_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "created_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_findings", + "name": "updated_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "run_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "agent_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "cost_usd", + "position": 3, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "sandbox_fee_usd", + "position": 4, + "type": "double precision", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_reservations", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "org_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "monthly_spend_limit_usd", + "position": 1, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "discovery_model", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "validation_model", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "updated_by", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_org_settings", + "name": "updated_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "run_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "fingerprint", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "verdict", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "overall_severity", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "estimated_severity", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_run_findings", + "name": "record", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "target_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "source_ref", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "profile", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "scope_paths", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "budget", + "position": 7, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "metadata", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "status", + "position": 9, + "type": "text", + "notNull": true, + "default": "'in_progress'", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "incomplete_reason", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "architecture_md", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "reports", + "position": 12, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "execution_profile", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "invocations_spent", + "position": 14, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "input_tokens", + "position": 15, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "output_tokens", + "position": 16, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "cost_usd", + "position": 17, + "type": "double precision", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "sandbox_usd", + "position": 18, + "type": "double precision", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "discovery_model", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "validation_model", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "workflow_instance_id", + "position": 21, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "started_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "finished_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_runs", + "name": "error", + "position": 24, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "repository", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "vcs_repository_id", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "branch", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "enabled", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "profile", + "position": 6, + "type": "text", + "notNull": true, + "default": "'deep'", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "scope_paths", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "slice_interval_hours", + "position": 8, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "revalidate_on_push", + "position": 9, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "budget_per_run", + "position": 10, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "monthly_invocation_budget", + "position": 11, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "discovery_model", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "validation_model", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "created_by", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_targets", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "run_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "coverage_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "status", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "wave", + "position": 4, + "type": "integer", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "agent_id", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "security_audit_units", + "name": "unit", + "position": 6, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "description", + "position": 3, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "platform", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "kind", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "key_hash", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "key_prefix", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "allowed_signals", + "position": 8, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "rate_limit_rps", + "position": 9, + "type": "integer", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "resource_attributes", + "position": 10, + "type": "jsonb", + "notNull": true, + "default": "'[]'::jsonb", + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "expires_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "paused_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "paused_until", + "position": 13, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_used_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_rejected_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_rejection", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_poll_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_poll_ok_at", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "last_poll_error", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "disconnected_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "poll_resources", + "position": 21, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "rolled_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "created_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "updated_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "created_by", + "position": 25, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "sources", + "name": "updated_by", + "position": 26, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "page_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "slug", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "delete_snapshot", + "position": 4, + "type": "boolean", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_cleanups", + "name": "created_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "page_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "name", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "description", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "group_name", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "position", + "position": 6, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "monitor_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "manual_status", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "show_uptime", + "position": 9, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_components", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_components", + "name": "incident_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_components", + "name": "component_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_components", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "incident_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "status", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "body", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "source", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "created_by", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "overlay_entry_id", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incident_updates", + "name": "created_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "page_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "title", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "impact", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "status", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "published", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "alert_incident_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "scheduled_start", + "position": 9, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "scheduled_end", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "started_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "resolved_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "created_by", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "created_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_page_incidents", + "name": "updated_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "slug", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "description", + "position": 4, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "website_url", + "position": 5, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "logo_url", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "privacy_url", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "terms_url", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "theme", + "position": 9, + "type": "jsonb", + "notNull": true, + "default": "'{\"preset\":\"default\",\"appearance\":\"auto\",\"accent\":null,\"background\":null,\"text\":null,\"card\":null,\"border\":null,\"radius\":\"md\",\"font\":\"sans\"}'", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "show_powered_by", + "position": 10, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "auto_incidents", + "position": 11, + "type": "text", + "notNull": true, + "default": "'off'", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "break_glass_token_sha256", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "break_glass_token_suffix", + "position": 13, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "draft", + "position": 14, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "revision", + "position": 15, + "type": "bigint", + "notNull": true, + "default": "1", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "published_revision", + "position": 16, + "type": "bigint", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "published_at", + "position": 17, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "last_publish_error", + "position": 18, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "published_hash", + "position": 19, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "publish_attempted_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "created_by", + "position": 21, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "created_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "status_pages", + "name": "updated_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "supabase_organization_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "supabase_organization_slug", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "supabase_organization_name", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "connected_by_user_id", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "discovery_lease_until", + "position": 7, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "last_discovery_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "last_discovery_error", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "created_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_connections", + "name": "updated_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "project_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "advisors_json", + "position": 2, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "top_queries_json", + "position": 3, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "advisors_checked_at", + "position": 4, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "queries_checked_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "advisors_error", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "queries_error", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "lease_until", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "created_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_project_insights", + "name": "updated_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "connection_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "project_ref", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "region", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "status", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "metrics_enabled", + "position": 7, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "scrape_target_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_enabled", + "position": 9, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_id", + "position": 10, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_source_id", + "position": 11, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drain_error", + "position": 12, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drains_json", + "position": 13, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "log_drains_checked_at", + "position": 14, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_enabled", + "position": 15, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_watermark_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_boundary_ids", + "position": 17, + "type": "jsonb", + "notNull": true, + "default": "'[]'", + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_lease_until", + "position": 18, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_last_polled_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "logs_error", + "position": 20, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "last_seen_at", + "position": 21, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "removed_at", + "position": 22, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "created_at", + "position": 23, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "supabase_projects", + "name": "updated_at", + "position": 24, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "monitor_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "day", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "org_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "checks", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "failures", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "down_checks", + "position": 5, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_daily", + "name": "last_check_at", + "position": 6, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "monitor_id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "status", + "position": 2, + "type": "text", + "notNull": true, + "default": "'pending'", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "consecutive_failures", + "position": 3, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "consecutive_successes", + "position": 4, + "type": "integer", + "notNull": true, + "default": "0", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "status_changed_at", + "position": 5, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "last_check_json", + "position": 6, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "recent_json", + "position": 7, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "last_ping_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitor_states", + "name": "updated_at", + "position": 9, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "name", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "kind", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "config_json", + "position": 4, + "type": "jsonb", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "interval_seconds", + "position": 5, + "type": "integer", + "notNull": true, + "default": "60", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "failures_to_down", + "position": 6, + "type": "integer", + "notNull": true, + "default": "2", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "service_name", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "alert_rule_id", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "heartbeat_token", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "enabled", + "position": 10, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "created_by", + "position": 11, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "created_at", + "position": 12, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "uptime_monitors", + "name": "updated_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "repository_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "sha", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "message", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_name", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_email", + "position": 7, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_login", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "author_avatar_url", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "authored_at", + "position": 10, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "committed_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "html_url", + "position": 12, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_commits", + "name": "created_at", + "position": 13, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "source_id", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "provider", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "external_installation_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "account_login", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "account_type", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "external_account_id", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "account_avatar_url", + "position": 8, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "repository_selection", + "position": 9, + "type": "text", + "notNull": true, + "default": "'all'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "status", + "position": 10, + "type": "text", + "notNull": true, + "default": "'active'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "suspended_at", + "position": 11, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "ci_traces_enabled", + "position": 12, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "repository_discovery", + "position": 13, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "installed_by_user_id", + "position": 14, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "created_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_installations", + "name": "updated_at", + "position": 16, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "installation_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "external_repo_id", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "owner", + "position": 5, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "name", + "position": 6, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "full_name", + "position": 7, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "default_branch", + "position": 8, + "type": "text", + "notNull": true, + "default": "'main'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "tracked_branch", + "position": 9, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "html_url", + "position": 10, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "is_private", + "position": 11, + "type": "boolean", + "notNull": true, + "default": "true", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "is_archived", + "position": 12, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "status", + "position": 13, + "type": "text", + "notNull": true, + "default": "'active'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "sync_status", + "position": 14, + "type": "text", + "notNull": true, + "default": "'pending'", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "last_synced_at", + "position": 15, + "type": "timestamp with time zone", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "last_sync_error", + "position": 16, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "pr_review_enabled", + "position": 17, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "pr_review_config", + "position": 18, + "type": "jsonb", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "created_at", + "position": 19, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repositories", + "name": "updated_at", + "position": 20, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "id", + "position": 0, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "org_id", + "position": 1, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "provider", + "position": 2, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "repository_id", + "position": 3, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "name", + "position": 4, + "type": "text", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "is_default", + "position": 5, + "type": "boolean", + "notNull": true, + "default": "false", + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "head_sha", + "position": 6, + "type": "text", + "notNull": false, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "created_at", + "position": 7, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "column", + "table": "vcs_repository_branches", + "name": "updated_at", + "position": 8, + "type": "timestamp with time zone", + "notNull": true, + "default": null, + "identity": null + }, + { + "kind": "index", + "table": "actors", + "name": "actors_org_agent_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"agent_name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "actors", + "name": "actors_org_type_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"type\"" + ], + "where": null + }, + { + "kind": "index", + "table": "actors", + "name": "actors_org_user_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "agent_feedback", + "name": "agent_feedback_kind_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"kind\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "agent_feedback", + "name": "agent_feedback_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_claim_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"claim_expires_at\"", + "\"scheduled_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_delivery_attempt_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"delivery_key\"", + "\"attempt_number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"scheduled_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_delivery_events", + "name": "alert_delivery_events_org_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_destinations", + "name": "alert_destinations_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_destinations", + "name": "alert_destinations_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_destinations", + "name": "alert_destinations_org_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_escalation_runs", + "name": "alert_escalation_runs_live_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"next_step_at\"" + ], + "where": "\"status\" in ('active', 'acknowledged')" + }, + { + "kind": "index", + "table": "alert_escalation_runs", + "name": "alert_escalation_runs_live_incident_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"incident_id\"" + ], + "where": "\"status\" IN ('active', 'acknowledged')" + }, + { + "kind": "index", + "table": "alert_escalation_runs", + "name": "alert_escalation_runs_org_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incident_events", + "name": "alert_incident_events_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_incident_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"incident_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_open_group_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"rule_id\"", + "\"group_key\"" + ], + "where": "\"status\" = 'open'" + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"error_issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_rule_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"rule_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_incidents", + "name": "alert_incidents_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_linear_issues", + "name": "alert_linear_issues_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_linear_issues", + "name": "alert_linear_issues_thread_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"destination_id\"", + "\"thread_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rule_claims", + "name": "alert_rule_claims_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rule_states", + "name": "alert_rule_states_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rules", + "name": "alert_rules_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rules", + "name": "alert_rules_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "alert_rules", + "name": "alert_rules_org_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_detector_states", + "name": "anomaly_detector_states_evaluated_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"last_evaluated_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_detector_states", + "name": "anomaly_detector_states_open_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"open_incident_id\"" + ], + "where": "\"open_incident_id\" is not null" + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_open_detector_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"detector_key\"" + ], + "where": "\"status\" = 'open'" + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_detector_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"detector_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"error_issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_status_triggered_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"", + "\"last_triggered_at\"", + "\"id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "anomaly_incidents", + "name": "anomaly_incidents_org_triggered_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"last_triggered_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "api_keys", + "name": "api_keys_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "api_keys", + "name": "api_keys_org_id_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "aws_connections", + "name": "aws_connections_org_account_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"account_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "aws_connections", + "name": "aws_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "aws_connections", + "name": "aws_connections_stream_secret_hash_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"stream_secret_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cancellation_reviews", + "name": "cancellation_reviews_subscription_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"plan_id\"", + "\"subscription_started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_identities", + "name": "chat_identities_org_connector_external_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"connector\"", + "\"external_user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_identities", + "name": "chat_identities_org_connector_user_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"connector\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_identities", + "name": "chat_identities_org_user_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_workspaces", + "name": "chat_workspaces_connector_external_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"connector\"", + "\"external_workspace_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "chat_workspaces", + "name": "chat_workspaces_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cli_device_authorizations", + "name": "cli_device_authorizations_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cli_device_authorizations", + "name": "cli_device_authorizations_user_code_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"user_code_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_analytics_state", + "name": "cf_analytics_state_org_account_dataset_zone_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"account_id\"", + "\"dataset\"", + "\"zone_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_analytics_state", + "name": "cf_analytics_state_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_hyperdrive_configs", + "name": "cloudflare_hyperdrive_configs_org_config_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"config_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_hyperdrive_configs", + "name": "cloudflare_hyperdrive_configs_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_secret_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"secret_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_id_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_live_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"", + "coalesce(widget_id, '')" + ], + "where": "revoked_at is null" + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_org_dashboard_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_shares", + "name": "dashboard_shares_token_hash_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"token_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_versions", + "name": "dashboard_versions_org_dashboard_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"", + "\"version_number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboard_versions", + "name": "dashboard_versions_org_dashboard_version_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dashboard_id\"", + "\"version_number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboards", + "name": "dashboards_org_name_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dashboards", + "name": "dashboards_org_updated_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"updated_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_findings", + "name": "dependency_findings_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"lockfile_path\"", + "\"ecosystem\"", + "\"package_name\"", + "\"advisory_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_findings", + "name": "dependency_findings_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_findings", + "name": "dependency_findings_org_updated_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"updated_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scan_matches", + "name": "dependency_scan_matches_org_scan_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"scan_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scans", + "name": "dependency_scans_org_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scans", + "name": "dependency_scans_repo_commit_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"commit_sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "dependency_scans", + "name": "dependency_scans_repo_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "device_credentials", + "name": "device_credentials_jkt_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"jkt\"" + ], + "where": null + }, + { + "kind": "index", + "table": "device_credentials", + "name": "device_credentials_live_installation_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"installation_id\"" + ], + "where": "\"revoked_at\" IS NULL" + }, + { + "kind": "index", + "table": "device_credentials", + "name": "device_credentials_user_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "device_proof_replays", + "name": "device_proof_replays_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "digest_subscriptions", + "name": "digest_subscriptions_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "digest_subscriptions", + "name": "digest_subscriptions_org_user_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_fingerprint_candidates", + "name": "error_fingerprint_candidates_last_seen_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"last_seen_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_incidents", + "name": "error_incidents_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_incidents", + "name": "error_incidents_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"", + "\"last_triggered_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_events", + "name": "error_issue_events_actor_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"actor_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_events", + "name": "error_issue_events_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_events", + "name": "error_issue_events_type_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"type\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_fingerprints", + "name": "error_issue_fingerprints_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_pull_requests", + "name": "error_issue_pull_requests_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_pull_requests", + "name": "error_issue_pull_requests_issue_pr_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"", + "\"provider\"", + "\"repo_full_name\"", + "\"number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_pull_requests", + "name": "error_issue_pull_requests_repo_number_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"provider\"", + "\"repo_full_name\"", + "\"number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_verifications", + "name": "error_issue_verifications_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"verify_after\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_verifications", + "name": "error_issue_verifications_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issue_verifications", + "name": "error_issue_verifications_open_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": "\"status\" in ('waiting', 'running')" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_lease_expiry_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"lease_expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_archived_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"archived_at\"" + ], + "where": "\"archived_at\" is not null" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_assignee_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"assigned_actor_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_fp_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"fingerprint_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_fp_version_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"fingerprint_version\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_live_seen_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"last_seen_at\"", + "\"id\"" + ], + "where": "\"archived_at\" is null" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_merged_into_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"merged_into_issue_id\"" + ], + "where": "\"merged_into_issue_id\" is not null" + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_severity_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"severity\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_issues", + "name": "error_issues_org_workflow_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"workflow_state\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_notification_deliveries", + "name": "error_notification_deliveries_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"scheduled_at\"", + "\"claim_expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_notification_deliveries", + "name": "error_notification_deliveries_key_destination_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"delivery_key\"", + "\"destination_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_notification_deliveries", + "name": "error_notification_deliveries_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "error_tick_states", + "name": "error_tick_states_claim_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"claim_expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "escalation_chains", + "name": "escalation_chains_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "gcp_connectors", + "name": "gcp_connectors_org_scope_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"scope_type\"", + "\"scope_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "gcp_connectors", + "name": "gcp_connectors_secret_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"secret_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "gcp_connectors", + "name": "gcp_connectors_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_incident_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_kind\"", + "\"incident_id\"" + ], + "where": "\"incident_id\" is not null" + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "investigations", + "name": "investigations_org_status_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"status\"" + ], + "where": null + }, + { + "kind": "index", + "table": "issue_escalations", + "name": "issue_escalations_dedupe_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"dedupe_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "issue_escalations", + "name": "issue_escalations_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"status\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "issue_escalations", + "name": "issue_escalations_org_issue_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"issue_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "live_activities", + "name": "live_activities_device_incident_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"device_id\"", + "\"incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "live_activities", + "name": "live_activities_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_authorizations", + "name": "mcp_oauth_authorizations_code_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"authorization_code_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_authorizations", + "name": "mcp_oauth_authorizations_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_refresh_tokens", + "name": "mcp_oauth_refresh_tokens_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_refresh_tokens", + "name": "mcp_oauth_refresh_tokens_family_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"family_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mcp_oauth_refresh_tokens", + "name": "mcp_oauth_refresh_tokens_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"token_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mobile_devices", + "name": "mobile_devices_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mobile_devices", + "name": "mobile_devices_org_platform_token_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"platform\"", + "\"token\"" + ], + "where": null + }, + { + "kind": "index", + "table": "mobile_devices", + "name": "mobile_devices_user_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"user_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_auth_states", + "name": "oauth_auth_states_expires_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"expires_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_connections", + "name": "oauth_connections_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_connections", + "name": "oauth_connections_org_provider_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"provider\"" + ], + "where": null + }, + { + "kind": "index", + "table": "oauth_connections", + "name": "oauth_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_billing_links", + "name": "org_billing_links_billing_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"billing_org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_attribute_mappings", + "name": "org_ingest_attribute_mappings_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_keys", + "name": "org_ingest_keys_private_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"private_key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_keys", + "name": "org_ingest_keys_public_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"public_key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_keys", + "name": "org_ingest_keys_public_key_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"public_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_rule_stats", + "name": "org_ingest_rule_stats_org_bucket_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"bucket\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_ingest_rules", + "name": "org_ingest_rules_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_recommendation_issues", + "name": "org_recommendation_issues_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "org_recommendation_issues", + "name": "org_recommendation_issues_org_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"recommendation_key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_connections", + "name": "planetscale_connections_org_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_connections", + "name": "planetscale_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_databases", + "name": "planetscale_databases_org_db_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"database_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_databases", + "name": "planetscale_databases_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_events", + "name": "planetscale_events_dedupe_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"database_name\"", + "\"event_type\"", + "\"external_id\"", + "\"occurred_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_events", + "name": "planetscale_events_org_db_time_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"database_name\"", + "\"occurred_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_events", + "name": "planetscale_events_org_time_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"occurred_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_issue_receipts", + "name": "planetscale_issue_receipts_processed_at_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"processed_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_poll_state", + "name": "planetscale_poll_state_org_dataset_db_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"dataset\"", + "\"database_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "planetscale_poll_state", + "name": "planetscale_poll_state_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_edits", + "name": "pr_review_edits_reply_seq_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"reply_id\"", + "\"seq\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_finding_embeddings", + "name": "pr_review_finding_embeddings_org_model_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"model\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_findings", + "name": "pr_review_findings_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_findings", + "name": "pr_review_findings_pr_handle_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"", + "\"handle\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_merge_steps", + "name": "pr_review_merge_steps_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_merge_steps", + "name": "pr_review_merge_steps_pr_key_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"", + "\"key\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_replies", + "name": "pr_review_replies_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_review_replies", + "name": "pr_review_replies_repo_comment_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"comment_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_post_merge_due_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"post_merge_after\"" + ], + "where": "\"post_merge_status\" = 'waiting'" + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_repo_number_head_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"", + "\"head_sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "pr_reviews", + "name": "pr_reviews_repo_number_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"number\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_connections", + "name": "railway_connections_org_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_connections", + "name": "railway_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_environments", + "name": "railway_environments_connection_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"connection_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "railway_environments", + "name": "railway_environments_org_env_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"environment_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_target_checks", + "name": "scrape_target_checks_target_checked_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"target_id\"", + "\"checked_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_targets", + "name": "scrape_targets_org_enabled_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"enabled\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_targets", + "name": "scrape_targets_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "scrape_targets", + "name": "scrape_targets_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_events", + "name": "security_audit_events_run_at_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"run_id\"", + "\"at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_findings", + "name": "security_audit_findings_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_findings", + "name": "security_audit_findings_target_fingerprint_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"target_id\"", + "\"fingerprint\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_org_reservations", + "name": "security_audit_org_reservations_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_runs", + "name": "security_audit_runs_one_running_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"target_id\"" + ], + "where": "\"finished_at\" IS NULL" + }, + { + "kind": "index", + "table": "security_audit_runs", + "name": "security_audit_runs_org_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_runs", + "name": "security_audit_runs_target_started_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"target_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_targets", + "name": "security_audit_targets_org_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "security_audit_targets", + "name": "security_audit_targets_org_repo_branch_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"repository\"", + "\"branch\"" + ], + "where": null + }, + { + "kind": "index", + "table": "sources", + "name": "sources_key_hash_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"key_hash\"" + ], + "where": null + }, + { + "kind": "index", + "table": "sources", + "name": "sources_org_default_kind_unique", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": "\"kind\" = 'default'" + }, + { + "kind": "index", + "table": "sources", + "name": "sources_org_id_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_cleanups", + "name": "status_page_cleanups_created_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_components", + "name": "status_page_components_monitor_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"monitor_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_components", + "name": "status_page_components_page_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"page_id\"", + "\"position\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incident_components", + "name": "status_page_incident_components_component_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"component_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incident_updates", + "name": "status_page_incident_updates_incident_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"incident_id\"", + "\"created_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incident_updates", + "name": "status_page_incident_updates_overlay_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"overlay_entry_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incidents", + "name": "status_page_incidents_alert_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"alert_incident_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_page_incidents", + "name": "status_page_incidents_page_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"page_id\"", + "\"started_at\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_pages", + "name": "status_pages_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_pages", + "name": "status_pages_slug_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"slug\"" + ], + "where": null + }, + { + "kind": "index", + "table": "status_pages", + "name": "status_pages_unpublished_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"revision\"", + "\"published_revision\"" + ], + "where": "revision > published_revision" + }, + { + "kind": "index", + "table": "supabase_connections", + "name": "supabase_connections_org_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_connections", + "name": "supabase_connections_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_project_insights", + "name": "supabase_project_insights_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_projects", + "name": "supabase_projects_connection_ref_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"connection_id\"", + "\"project_ref\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_projects", + "name": "supabase_projects_log_drain_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"log_drain_source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "supabase_projects", + "name": "supabase_projects_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitor_daily", + "name": "uptime_monitor_daily_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitor_states", + "name": "uptime_monitor_states_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitors", + "name": "uptime_monitors_enabled_kind_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"enabled\"", + "\"kind\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitors", + "name": "uptime_monitors_heartbeat_token_unq", + "unique": true, + "method": "btree", + "columns": [ + "\"heartbeat_token\"" + ], + "where": null + }, + { + "kind": "index", + "table": "uptime_monitors", + "name": "uptime_monitors_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_commits", + "name": "vcs_commits_org_sha_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_commits", + "name": "vcs_commits_repo_sha_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"sha\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_installations", + "name": "vcs_installations_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_installations", + "name": "vcs_installations_provider_external_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"provider\"", + "\"external_installation_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_installations", + "name": "vcs_installations_source_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"source_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repositories", + "name": "vcs_repositories_installation_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"installation_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repositories", + "name": "vcs_repositories_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repositories", + "name": "vcs_repositories_org_repo_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"org_id\"", + "\"provider\"", + "\"external_repo_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repository_branches", + "name": "vcs_repository_branches_org_idx", + "unique": false, + "method": "btree", + "columns": [ + "\"org_id\"" + ], + "where": null + }, + { + "kind": "index", + "table": "vcs_repository_branches", + "name": "vcs_repository_branches_repo_name_idx", + "unique": true, + "method": "btree", + "columns": [ + "\"repository_id\"", + "\"name\"" + ], + "where": null + }, + { + "kind": "foreign_key", + "table": "alert_escalation_runs", + "name": "alert_escalation_runs_incident_id_alert_incidents_id_fk", + "columns": [ + "incident_id" + ], + "foreignTable": "alert_incidents", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "alert_incident_events", + "name": "alert_incident_events_incident_id_alert_incidents_id_fk", + "columns": [ + "incident_id" + ], + "foreignTable": "alert_incidents", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "alert_linear_issues", + "name": "alert_linear_issues_destination_id_alert_destinations_id_fk", + "columns": [ + "destination_id" + ], + "foreignTable": "alert_destinations", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "aws_connections", + "name": "aws_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "cloudflare_logpush_connectors", + "name": "cloudflare_logpush_connectors_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "SET NULL", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "dashboard_shares", + "name": "dashboard_shares_dashboard_fk", + "columns": [ + "org_id", + "dashboard_id" + ], + "foreignTable": "dashboards", + "foreignColumns": [ + "org_id", + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "gcp_connectors", + "name": "gcp_connectors_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "gcp_resources", + "name": "gcp_resources_connector_id_gcp_connectors_id_fk", + "columns": [ + "connector_id" + ], + "foreignTable": "gcp_connectors", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "oauth_connections", + "name": "oauth_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "planetscale_connections", + "name": "planetscale_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "railway_connections", + "name": "railway_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "scrape_target_checks", + "name": "scrape_target_checks_target_id_scrape_targets_id_fk", + "columns": [ + "target_id" + ], + "foreignTable": "scrape_targets", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "scrape_targets", + "name": "scrape_targets_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_components", + "name": "status_page_components_monitor_id_uptime_monitors_id_fk", + "columns": [ + "monitor_id" + ], + "foreignTable": "uptime_monitors", + "foreignColumns": [ + "id" + ], + "onDelete": "SET NULL", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_components", + "name": "status_page_components_page_id_status_pages_id_fk", + "columns": [ + "page_id" + ], + "foreignTable": "status_pages", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incident_components", + "name": "status_page_incident_components_Bnqlt6wwKJty_fk", + "columns": [ + "component_id" + ], + "foreignTable": "status_page_components", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incident_components", + "name": "status_page_incident_components_tBn6mEz4FenY_fk", + "columns": [ + "incident_id" + ], + "foreignTable": "status_page_incidents", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incident_updates", + "name": "status_page_incident_updates_3LDDUAhznHPM_fk", + "columns": [ + "incident_id" + ], + "foreignTable": "status_page_incidents", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "status_page_incidents", + "name": "status_page_incidents_page_id_status_pages_id_fk", + "columns": [ + "page_id" + ], + "foreignTable": "status_pages", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_connections", + "name": "supabase_connections_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_project_insights", + "name": "supabase_project_insights_project_id_supabase_projects_id_fk", + "columns": [ + "project_id" + ], + "foreignTable": "supabase_projects", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_projects", + "name": "supabase_projects_connection_id_supabase_connections_id_fk", + "columns": [ + "connection_id" + ], + "foreignTable": "supabase_connections", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "supabase_projects", + "name": "supabase_projects_log_drain_source_id_sources_id_fk", + "columns": [ + "log_drain_source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "SET NULL", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "uptime_monitor_daily", + "name": "uptime_monitor_daily_monitor_id_uptime_monitors_id_fk", + "columns": [ + "monitor_id" + ], + "foreignTable": "uptime_monitors", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "uptime_monitor_states", + "name": "uptime_monitor_states_monitor_id_uptime_monitors_id_fk", + "columns": [ + "monitor_id" + ], + "foreignTable": "uptime_monitors", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + }, + { + "kind": "foreign_key", + "table": "vcs_installations", + "name": "vcs_installations_source_id_sources_id_fk", + "columns": [ + "source_id" + ], + "foreignTable": "sources", + "foreignColumns": [ + "id" + ], + "onDelete": "CASCADE", + "onUpdate": "NO ACTION" + } + ] +} diff --git a/packages/db/src/tables/escalation-chains.ts b/packages/db/src/tables/escalation-chains.ts new file mode 100644 index 0000000000..c4ce31df5d --- /dev/null +++ b/packages/db/src/tables/escalation-chains.ts @@ -0,0 +1,88 @@ +import * as PG from "@maple-dev/effect-orm/postgres" +import { AlertEscalationRunStatus, AlertSeverity, EscalationSteps } from "@maple/domain/http" +import { AlertEscalationRunId, AlertIncidentId, EscalationChainId, OrgId } from "@maple/domain/primitives" +import { Schema } from "effect" +import { AlertIncidents } from "./alerts" + +/** + * Ordered notification steps a severity tier can start: notify, wait for an ack, then the next + * step, the whole chain repeating `repeat_count` more times. Distinct from `issue_escalation_policies`, + * which routes a triage severity once and has no timer. + */ +export const EscalationChains = PG.table("escalation_chains", { + columns: { + id: PG.brand(PG.text, EscalationChainId), + orgId: PG.column(PG.brand(PG.text, OrgId), { name: "org_id" }), + name: PG.text, + description: PG.nullable(PG.text), + stepsJson: PG.column(PG.jsonb(EscalationSteps), { name: "steps_json" }), + repeatCount: PG.column(PG.int4, { name: "repeat_count", default: 0 }), + createdAt: PG.column(PG.timestamptzMillis, { name: "created_at" }), + updatedAt: PG.column(PG.timestamptzMillis, { name: "updated_at" }), + createdBy: PG.column(PG.text, { name: "created_by" }), + updatedBy: PG.column(PG.text, { name: "updated_by" }), + }, + primaryKey: ["id"], + indexes: [PG.index("escalation_chains_org_idx", ["orgId"])], + tenantColumn: "orgId", +}) + +const alertEscalationRunsColumns = { + id: PG.brand(PG.text, AlertEscalationRunId), + orgId: PG.column(PG.brand(PG.text, OrgId), { name: "org_id" }), + incidentId: PG.column(PG.brand(PG.text, AlertIncidentId), { name: "incident_id" }), + /** No foreign key: a deleted chain's running escalations finish from their snapshot. */ + chainId: PG.column(PG.brand(PG.text, EscalationChainId), { name: "chain_id" }), + chainName: PG.column(PG.text, { name: "chain_name" }), + /** The tier whose chain this is. */ + severity: PG.brand(PG.text, AlertSeverity), + /** The chain as it stood when the run started, so an edit cannot shift a page in flight. */ + stepsJson: PG.column(PG.jsonb(EscalationSteps), { name: "steps_json" }), + repeatCount: PG.column(PG.int4, { name: "repeat_count" }), + /** The next step to send and the pass it belongs to, both from 0. */ + stepIndex: PG.column(PG.int4, { name: "step_index", default: 0 }), + passIndex: PG.column(PG.int4, { name: "pass_index", default: 0 }), + nextStepAt: PG.column(PG.timestamptzMillis, { name: "next_step_at" }), + status: PG.brand(PG.text, AlertEscalationRunStatus), + /** The trigger notification the run opened with; every step resends it. */ + payloadJson: PG.column(PG.jsonb(Schema.Record(Schema.String, Schema.Unknown)), { name: "payload_json" }), + stepsSent: PG.column(PG.int4, { name: "steps_sent", default: 0 }), + lastStepAt: PG.column(PG.nullable(PG.timestamptzMillis), { name: "last_step_at" }), + endedAt: PG.column(PG.nullable(PG.timestamptzMillis), { name: "ended_at" }), + createdAt: PG.column(PG.timestamptzMillis, { name: "created_at" }), + updatedAt: PG.column(PG.timestamptzMillis, { name: "updated_at" }), +} +type AlertEscalationRunsColumns = PG.ColumnsOf + +/** One incident's walk through a chain. The scheduler advances it one step at a time. */ +export const AlertEscalationRuns = PG.table("alert_escalation_runs", { + columns: alertEscalationRunsColumns, + primaryKey: ["id"], + indexes: [ + PG.index("alert_escalation_runs_org_incident_idx", ["orgId", "incidentId"]), + // The scheduler's scan: live runs only, which are a small slice of the table. + PG.index("alert_escalation_runs_live_idx", ["status", "nextStepAt"], { + where: `"status" in ('active', 'acknowledged')`, + }), + // At most one live run per incident: a newer tier's chain supersedes the old one. + PG.uniqueIndex( + "alert_escalation_runs_live_incident_idx", + ["incidentId"], + { + where: ($) => $.status.in_("active", "acknowledged"), + }, + ), + ], + foreignKeys: [ + PG.foreignKey({ + columns: ["incidentId"], + references: AlertIncidents, + foreignColumns: ["id"], + onDelete: "cascade", + }), + ], + tenantColumn: "orgId", +}) + +export type EscalationChainRow = PG.SelectRowOf +export type AlertEscalationRunRow = PG.SelectRowOf diff --git a/packages/db/src/tables/index.ts b/packages/db/src/tables/index.ts index ac85205803..43c52f1687 100644 --- a/packages/db/src/tables/index.ts +++ b/packages/db/src/tables/index.ts @@ -41,6 +41,7 @@ import { ErrorIssueVerifications, } from "./errors" import { IssueEscalationPolicies, IssueEscalations } from "./escalations" +import { AlertEscalationRuns, EscalationChains } from "./escalation-chains" import { AwsConnections } from "./aws-connections" import { GcpConnectors } from "./gcp-connectors" import { GcpResources } from "./gcp-resources" @@ -125,6 +126,7 @@ export * from "./device-credentials" export * from "./digest" export * from "./errors" export * from "./escalations" +export * from "./escalation-chains" export * from "./aws-connections" export * from "./gcp-connectors" export * from "./gcp-resources" @@ -198,6 +200,8 @@ export const allTables: ReadonlyArray> = [ ErrorIssueVerifications, IssueEscalationPolicies, IssueEscalations, + EscalationChains, + AlertEscalationRuns, GcpConnectors, GcpResources, AwsConnections, diff --git a/packages/domain/src/http/alerts.ts b/packages/domain/src/http/alerts.ts index e9b66f54ba..850faa9440 100644 --- a/packages/domain/src/http/alerts.ts +++ b/packages/domain/src/http/alerts.ts @@ -9,6 +9,7 @@ import { ChatConnectorId, ChatWorkspaceId, ErrorIssueId, + EscalationChainId, IsoDateTimeString, PostgresTransactionId, RoleName, @@ -245,6 +246,8 @@ export const AlertThreshold = Schema.Struct({ /** Null recovers as soon as the trigger stops holding. */ recoveryValue: Schema.NullOr(Schema.Number), destinationIds: Schema.Array(AlertDestinationId), + /** Escalation chain an incident at this tier starts, on top of its destinations. */ + escalationChainId: Schema.optionalKey(Schema.NullOr(EscalationChainId)), }).annotate({ identifier: "@maple/AlertThreshold", title: "Alert Threshold" }) export type AlertThreshold = Schema.Schema.Type diff --git a/packages/domain/src/http/escalation-chains.ts b/packages/domain/src/http/escalation-chains.ts new file mode 100644 index 0000000000..177f617781 --- /dev/null +++ b/packages/domain/src/http/escalation-chains.ts @@ -0,0 +1,128 @@ +import { Schema } from "effect" +import { AlertDestinationId, AlertRuleId, EscalationChainId, IsoDateTimeString } from "../primitives" +import { HttpTaggedError } from "./error-policy" + +export const MAX_ESCALATION_STEPS = 10 +export const MAX_ESCALATION_STEP_TARGETS = 10 +export const MAX_ESCALATION_REPEATS = 5 +/** A day: a longer wait is a schedule, not an escalation. */ +export const MAX_ESCALATION_WAIT_MINUTES = 1440 + +/** Who a step notifies. */ +export const EscalationTarget = Schema.Struct({ + type: Schema.Literal("destination"), + destinationId: AlertDestinationId, +}).annotate({ identifier: "@maple/EscalationTarget", title: "Escalation Target" }) +export type EscalationTarget = Schema.Schema.Type + +export const EscalationWaitMinutes = Schema.Int.check( + Schema.isGreaterThanOrEqualTo(1), + Schema.isLessThanOrEqualTo(MAX_ESCALATION_WAIT_MINUTES), +) + +/** Notify every target, then wait `waitMinutes` for an acknowledgement before the next step. */ +export const EscalationStep = Schema.Struct({ + targets: Schema.NonEmptyArray(EscalationTarget).check(Schema.isMaxLength(MAX_ESCALATION_STEP_TARGETS)), + waitMinutes: EscalationWaitMinutes, +}).annotate({ identifier: "@maple/EscalationStep", title: "Escalation Step" }) +export type EscalationStep = Schema.Schema.Type + +export const EscalationSteps = Schema.NonEmptyArray(EscalationStep) + .check(Schema.isMaxLength(MAX_ESCALATION_STEPS)) + .annotate({ identifier: "@maple/EscalationSteps", title: "Escalation Steps" }) +export type EscalationSteps = Schema.Schema.Type + +/** Extra passes over the whole chain after the first; 0 runs it once. */ +export const EscalationRepeatCount = Schema.Int.check( + Schema.isGreaterThanOrEqualTo(0), + Schema.isLessThanOrEqualTo(MAX_ESCALATION_REPEATS), +) + +export const EscalationChainName = Schema.String.check( + Schema.isMinLength(1), + Schema.isTrimmed(), + Schema.isMaxLength(120), +) +export const EscalationChainDescription = Schema.String.check(Schema.isMaxLength(500)) + +export class EscalationChainDocument extends Schema.Class("EscalationChainDocument")( + { + id: EscalationChainId, + name: Schema.String, + description: Schema.NullOr(Schema.String), + steps: EscalationSteps, + repeatCount: Schema.Number, + /** Rules with a tier that starts this chain. */ + ruleIds: Schema.Array(AlertRuleId), + createdAt: IsoDateTimeString, + updatedAt: IsoDateTimeString, + }, +) {} + +export class EscalationChainsListResponse extends Schema.Class( + "EscalationChainsListResponse", +)({ + chains: Schema.Array(EscalationChainDocument), +}) {} + +export class EscalationChainCreateRequest extends Schema.Class( + "EscalationChainCreateRequest", +)({ + name: EscalationChainName, + description: Schema.optionalKey(Schema.NullOr(EscalationChainDescription)), + steps: EscalationSteps, + repeatCount: Schema.optionalKey(EscalationRepeatCount), +}) {} + +export class EscalationChainUpdateRequest extends Schema.Class( + "EscalationChainUpdateRequest", +)({ + name: Schema.optionalKey(EscalationChainName), + description: Schema.optionalKey(Schema.NullOr(EscalationChainDescription)), + steps: Schema.optionalKey(EscalationSteps), + repeatCount: Schema.optionalKey(EscalationRepeatCount), +}) {} + +/** How far an incident's escalation got. `active` waits on `nextStepAt`. */ +export const AlertEscalationRunStatus = Schema.Literals([ + "active", + "acknowledged", + "resolved", + "exhausted", + "superseded", +]).annotate({ identifier: "@maple/AlertEscalationRunStatus", title: "Alert Escalation Run Status" }) +export type AlertEscalationRunStatus = Schema.Schema.Type + +export class EscalationChainNotFoundError extends HttpTaggedError()( + "@maple/http/errors/EscalationChainNotFoundError", + { message: Schema.String, chainId: EscalationChainId }, + { + status: 404, + code: "escalation_chain_not_found", + title: "Escalation chain not found", + message: "No such escalation chain.", + param: "id", + retry: "never", + recovery: "none", + exposure: "redacted", + }, +) {} + +export class EscalationChainInUseError extends HttpTaggedError()( + "@maple/http/errors/EscalationChainInUseError", + { + message: Schema.String, + chainId: EscalationChainId, + ruleIds: Schema.Array(AlertRuleId), + ruleNames: Schema.Array(Schema.String), + }, + { + status: 409, + code: "escalation_chain_in_use", + title: "Escalation chain is in use", + message: "The escalation chain is used by one or more alert rules.", + retry: "never", + recovery: "fix_request", + exposure: "redacted", + }, +) {} diff --git a/packages/domain/src/http/index.ts b/packages/domain/src/http/index.ts index 2ce5160a73..08a06c00f6 100644 --- a/packages/domain/src/http/index.ts +++ b/packages/domain/src/http/index.ts @@ -19,6 +19,7 @@ export * from "./api-keys" export * from "./device-credentials" export * from "./audit-log" export * from "./alerts" +export * from "./escalation-chains" export * from "./agent-feedback" export * from "./mcp-connections" export * from "./mcp-tools" diff --git a/packages/primitives/src/index.ts b/packages/primitives/src/index.ts index d066156105..611ecaacc6 100644 --- a/packages/primitives/src/index.ts +++ b/packages/primitives/src/index.ts @@ -198,6 +198,10 @@ export type AlertDeliveryEventId = Schema.Schema.Type +export const EscalationChainId = MapleUuidId("@maple/EscalationChainId", "Escalation Chain ID") +export type EscalationChainId = Schema.Schema.Type +export const AlertEscalationRunId = MapleUuidId("@maple/AlertEscalationRunId", "Alert Escalation Run ID") +export type AlertEscalationRunId = Schema.Schema.Type export const MobileDeviceId = MapleUuidId("@maple/MobileDeviceId", "Mobile Device ID") export type MobileDeviceId = Schema.Schema.Type From 0a0e10a4a17e5a4906afe8352773190950ee0a40 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 12:48:59 +0100 Subject: [PATCH 10/29] test(mcp): acknowledge by inc_ id and assert the rendered text --- .../tools/acknowledge-alert-incident.test.ts | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts b/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts index e992aef5bb..7d5bc15175 100644 --- a/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts +++ b/apps/ai/src/mcp/tools/acknowledge-alert-incident.test.ts @@ -1,12 +1,14 @@ import { afterEach, assert, describe, it } from "@effect/vitest" import { ConfigProvider, Effect, Layer, Schema } from "effect" import { OrgId, UserId } from "@maple/domain/http" +import { encodePublicId, PublicIdPrefixes } from "@maple/domain/http/v2" import { Env } from "@maple/backend/platform/Env" import { EdgeCacheServiceLive } from "@maple/backend/platform/CacheBackendLive" import { cleanupTestDbs, createTestDb, executeSql, type TestDb } from "@maple/backend/platform/test-pglite" import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { CurrentMcpTenant } from "../lib/query-warehouse" import { registerAcknowledgeAlertIncidentTool } from "./acknowledge-alert-incident" +import { renderToolDoc } from "../lib/tool-doc" import { CurrentAuditActor } from "@maple/backend/services/auth/audit-actor" import type { McpToolError, McpToolRegistrar } from "./types" import type { McpToolRequirements } from "./runtime-requirements" @@ -23,12 +25,17 @@ type Invoke = ( params: Readonly>, ) => Effect.Effect -const acknowledgeTool = (): Invoke => { +/** Calls the tool's handler; with `rendered`, returns the text the model reads instead. */ +const acknowledgeTool = (rendered = false): Invoke => { let invoke: Invoke = () => Effect.die("unregistered") const registrar: McpToolRegistrar = { define: (spec) => { invoke = (params) => - Schema.decodeEffect(spec.parameters)(params).pipe(Effect.orDie, Effect.flatMap(spec.handler)) + Schema.decodeEffect(spec.parameters)(params).pipe( + Effect.orDie, + Effect.flatMap(spec.handler), + Effect.map((output) => (rendered ? renderToolDoc(spec.render(output)) : output)), + ) }, } registerAcknowledgeAlertIncidentTool(registrar) @@ -87,7 +94,8 @@ describe("acknowledge_alert_incident", () => { const call = acknowledgeTool() return Effect.gen(function* () { yield* seed(testDb) - const acked = yield* call({ incident_id: OPEN }) + // The `inc_…` id the v2 API and the other tools print, not the internal UUID. + const acked = yield* call({ incident_id: encodePublicId(PublicIdPrefixes.alertIncident, OPEN) }) assert.deepInclude(acked, { id: OPEN, status: "open", @@ -105,6 +113,25 @@ describe("acknowledge_alert_incident", () => { }).pipe(Effect.provide(layerFor(testDb))) }) + it.effect("renders the change, a no-op notice, and the timeline as the next call", () => { + const testDb = createTestDb(trackedDbs) + const render = acknowledgeTool(true) + return Effect.gen(function* () { + yield* seed(testDb) + const acked = yield* render({ incident_id: OPEN }) + assert.include(acked, "## Incident Acknowledged") + assert.include(acked, "Checkout errors") + assert.include(acked, "someone via mcp") + assert.include(acked, "get_incident_timeline") + assert.include(acked, "00000000-0000-4000-8000-0000000000b0") + assert.notInclude(acked, "nothing changed") + assert.include(yield* render({ incident_id: OPEN }), "Already acknowledged; nothing changed.") + const unacked = yield* render({ incident_id: OPEN, acknowledged: false }) + assert.include(unacked, "## Incident Unacknowledged") + assert.notInclude(unacked, "someone via mcp") + }).pipe(Effect.provide(layerFor(testDb))) + }) + it.effect("names a resolved or unknown incident as invalid input on incident_id", () => { const testDb = createTestDb(trackedDbs) const call = acknowledgeTool() From eac581916132c7475fc5d03981eb01c4822069a8 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 12:52:13 +0100 Subject: [PATCH 11/29] test(alerts): escalation chains walk, repeat, stop on ack and resolve, and send each step once --- .../alerts/AlertsService.escalation.test.ts | 398 ++++++++++++++++++ .../services/alerts/alert-escalation-runs.ts | 4 +- .../src/services/alerts/alert-thresholds.ts | 11 +- 3 files changed, 408 insertions(+), 5 deletions(-) create mode 100644 packages/backend/src/services/alerts/AlertsService.escalation.test.ts diff --git a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts new file mode 100644 index 0000000000..1c5b0b86a9 --- /dev/null +++ b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts @@ -0,0 +1,398 @@ +import { + AlertDestinationInUseError, + AlertRuleUpsertRequest, + AlertValidationError, + EscalationChainCreateRequest, + EscalationChainId, + EscalationChainInUseError, + WebhookAlertDestinationConfig, + type AlertDestinationId, + type AlertThreshold, + type OrgId, + type UserId, +} from "@maple/domain/http" +import { afterEach, assert, describe, it } from "@effect/vitest" +import { Duration, Effect, Layer, Schema, Array as Arr, String as Str } from "effect" +import { TestClock } from "effect/testing" +import { cleanupTestDbs, createTestDb, executeSql, type TestDb } from "@maple/backend/platform/test-pglite" +import { AlertsService, type AlertsServiceApi } from "./AlertsService" +import { EscalationChainsService } from "./EscalationChainsService" +import { + DEFAULT_CLOCK_EPOCH_MS, + adminRoles, + asOrgId, + asUserId, + getError, + makeLayer, + makeWarehouseStub, + tracesRow, +} from "./alerts-service-test-support" + +const decodeChainId = Schema.decodeUnknownSync(EscalationChainId) +const trackedDbs: TestDb[] = [] +afterEach(() => cleanupTestDbs(trackedDbs)) + +const errorRateRows = (errorRate: number): ReadonlyArray> => [ + tracesRow({ + count: 200, + avgDuration: 40, + p50Duration: 20, + p95Duration: 120, + p99Duration: 240, + errorRate, + satisfiedCount: 180, + toleratingCount: 10, + apdexScore: 0.925, + }), +] + +/** `trigger https://example.com/slack` per webhook request, in send order. */ +const recordingFetch = (requests: Array) => + (async (input: RequestInfo | URL, init?: RequestInit) => { + const url = String(input instanceof Request ? input.url : input) + requests.push( + `${new Headers(init?.headers).get("x-maple-event-type")} ${Str.replace("https://example.com/", "")(url)}`, + ) + return new Response("ok", { status: 200 }) + }) as typeof fetch + +const layerFor = ( + testDb: TestDb, + state: { tracesAggregateRows: ReadonlyArray> }, + requests: Array, +) => + Layer.mergeAll( + makeLayer(testDb, makeWarehouseStub(state), { fetch: recordingFetch(requests) }), + EscalationChainsService.layer.pipe(Layer.provide(testDb.layer)), + ) + +const tick = (alerts: AlertsServiceApi, minutes = 1) => + Effect.gen(function* () { + yield* TestClock.adjust(Duration.minutes(minutes)) + yield* alerts.runSchedulerTick() + }) + +/** Ticks once a minute for `minutes` minutes. */ +const ticks = (alerts: AlertsServiceApi, minutes: number) => + Effect.forEach(Arr.range(1, minutes), () => tick(alerts), { discard: true }) + +const webhook = (alerts: AlertsServiceApi, orgId: OrgId, userId: UserId, name: string) => + alerts.createDestination( + orgId, + userId, + adminRoles, + new WebhookAlertDestinationConfig({ + type: "webhook", + name, + enabled: true, + url: `https://example.com/${name}`, + signingSecret: `${name}-secret`, + }), + ) + +const ruleRequest = (thresholds: Arr.NonEmptyReadonlyArray) => + new AlertRuleUpsertRequest({ + name: "Checkout error rate", + enabled: true, + serviceNames: ["checkout"], + signalType: "error_rate", + comparator: "gt", + thresholds, + windowMinutes: 5, + minimumSampleCount: 10, + consecutiveBreachesRequired: 2, + consecutiveHealthyRequired: 2, + renotifyIntervalMinutes: 60, + }) + +const tier = ( + severity: AlertThreshold["severity"], + value: number, + destinationIds: ReadonlyArray, + escalationChainId: EscalationChainId | null, +): AlertThreshold => ({ + severity, + value, + upper: null, + recoveryValue: null, + destinationIds, + escalationChainId, +}) + +/** "#team" on the tier; the chain notifies slack, waits 10 minutes, then pages, waits 15. */ +const setup = (orgName: string, options: { readonly repeatCount?: number } = {}) => + Effect.gen(function* () { + const alerts = yield* AlertsService + const chains = yield* EscalationChainsService + const orgId = asOrgId(orgName) + const userId = asUserId(`user_${orgName}`) + const team = yield* webhook(alerts, orgId, userId, "team") + const slack = yield* webhook(alerts, orgId, userId, "slack") + const pager = yield* webhook(alerts, orgId, userId, "pager") + const chain = yield* chains.create( + orgId, + userId, + adminRoles, + new EscalationChainCreateRequest({ + name: "API escalation", + steps: [ + { targets: [{ type: "destination", destinationId: slack.id }], waitMinutes: 10 }, + { targets: [{ type: "destination", destinationId: pager.id }], waitMinutes: 15 }, + ], + ...(options.repeatCount === undefined ? undefined : { repeatCount: options.repeatCount }), + }), + ) + const rule = yield* alerts.createRule( + orgId, + userId, + adminRoles, + ruleRequest([tier("critical", 5, [team.id], chain.id)]), + ) + return { alerts, chains, orgId, userId, team, slack, pager, chain, rule } + }) + +const runRows = (testDb: TestDb, orgId: OrgId) => + Effect.promise(() => + testDb.pglite.query<{ status: string; steps_sent: number; chain_name: string }>( + "select status, steps_sent, chain_name from alert_escalation_runs where org_id = $1 order by created_at", + [orgId], + ), + ).pipe(Effect.map((result) => result.rows)) + +const setAck = (testDb: TestDb, orgId: OrgId, acknowledged: boolean) => + Effect.promise(() => + executeSql( + testDb, + acknowledged + ? `update alert_incidents set acknowledged_at = now(), acknowledged_by_name = 'Ana', acknowledged_via = 'web' where org_id = $1` + : `update alert_incidents set acknowledged_at = null, acknowledged_by_name = null, acknowledged_via = null where org_id = $1`, + [orgId], + ), + ) + +describe("escalation chains", () => { + it.effect("pages the next step after the wait, once, and ends after the last step", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId } = yield* setup("org_esc_walk") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + // Opened: the tier's destination and the chain's first step, in the same tick. + assert.sameMembers(requests, ["trigger team", "trigger slack"]) + + yield* ticks(alerts, 9) + assert.lengthOf(requests, 2) + yield* tick(alerts) + assert.deepStrictEqual(requests.slice(2), ["trigger pager"]) + + yield* ticks(alerts, 20) + assert.lengthOf(requests, 3) + assert.deepStrictEqual(yield* runRows(testDb, orgId), [ + { status: "exhausted", steps_sent: 2, chain_name: "API escalation" }, + ]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("repeats the whole chain repeatCount more times", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId } = yield* setup("org_esc_repeat", { repeatCount: 1 }) + yield* alerts.runSchedulerTick() + yield* tick(alerts) + // slack at 0, pager at 10, slack again at 25, pager at 35. + yield* ticks(alerts, 40) + assert.deepStrictEqual( + Arr.filter(requests, (request) => request !== "trigger team"), + ["trigger slack", "trigger pager", "trigger slack", "trigger pager"], + ) + assert.strictEqual((yield* runRows(testDb, orgId))[0]?.status, "exhausted") + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("stops at an acknowledgement and picks up where it was after an unacknowledge", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId } = yield* setup("org_esc_ack") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + yield* setAck(testDb, orgId, true) + yield* ticks(alerts, 20) + // Nobody paged while Ana is on it. + assert.sameMembers(requests, ["trigger team", "trigger slack"]) + assert.strictEqual((yield* runRows(testDb, orgId))[0]?.status, "acknowledged") + + yield* setAck(testDb, orgId, false) + yield* tick(alerts) + assert.deepStrictEqual(requests.slice(2), ["trigger pager"]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("ends without paging once the incident resolves mid-wait", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId } = yield* setup("org_esc_resolve") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + state.tracesAggregateRows = errorRateRows(1) + yield* ticks(alerts, 3) + yield* ticks(alerts, 20) + assert.notInclude(requests, "trigger pager") + assert.strictEqual((yield* runRows(testDb, orgId))[0]?.status, "resolved") + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("sends a due step once when two scheduler ticks overlap", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId } = yield* setup("org_esc_race") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + yield* ticks(alerts, 9) + yield* TestClock.adjust(Duration.minutes(1)) + yield* Effect.all([alerts.runSchedulerTick(), alerts.runSchedulerTick()], { concurrency: 2 }) + assert.deepStrictEqual( + Arr.filter(requests, (request) => request === "trigger pager"), + ["trigger pager"], + ) + const rows = yield* Effect.promise(() => + testDb.pglite.query<{ n: number }>( + `select count(*)::int as n from alert_delivery_events d join alert_destinations x on x.id = d.destination_id + where d.org_id = $1 and x.name = 'pager'`, + [orgId], + ), + ) + assert.strictEqual(rows.rows[0]?.n, 1) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("notifies a destination in both the tier and the first step once", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const alerts = yield* AlertsService + const chains = yield* EscalationChainsService + const orgId = asOrgId("org_esc_dedupe") + const userId = asUserId("user_esc_dedupe") + const team = yield* webhook(alerts, orgId, userId, "team") + const chain = yield* chains.create( + orgId, + userId, + adminRoles, + new EscalationChainCreateRequest({ + name: "Team first", + steps: [{ targets: [{ type: "destination", destinationId: team.id }], waitMinutes: 5 }], + }), + ) + yield* alerts.createRule( + orgId, + userId, + adminRoles, + ruleRequest([tier("critical", 5, [team.id], chain.id)]), + ) + yield* alerts.runSchedulerTick() + yield* tick(alerts) + assert.deepStrictEqual(requests, ["trigger team"]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("restarts on the critical tier's own chain when a warning incident escalates", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const alerts = yield* AlertsService + const chains = yield* EscalationChainsService + const orgId = asOrgId("org_esc_supersede") + const userId = asUserId("user_esc_supersede") + const slack = yield* webhook(alerts, orgId, userId, "slack") + const pager = yield* webhook(alerts, orgId, userId, "pager") + const create = (name: string, destinationId: AlertDestinationId, repeatCount: number) => + chains.create( + orgId, + userId, + adminRoles, + new EscalationChainCreateRequest({ + name, + steps: [{ targets: [{ type: "destination", destinationId }], waitMinutes: 30 }], + repeatCount, + }), + ) + // The warning chain is still waiting to repeat when the incident escalates. + const warnChain = yield* create("Warn chain", slack.id, 1) + const pageChain = yield* create("Page chain", pager.id, 0) + yield* alerts.createRule( + orgId, + userId, + adminRoles, + ruleRequest([tier("critical", 20, [], pageChain.id), tier("warning", 5, [], warnChain.id)]), + ) + yield* alerts.runSchedulerTick() + yield* tick(alerts) + assert.deepStrictEqual(requests, ["trigger slack"]) + state.tracesAggregateRows = errorRateRows(30) + yield* ticks(alerts, 2) + assert.deepStrictEqual(requests, ["trigger slack", "trigger pager"]) + assert.deepStrictEqual( + Arr.map(yield* runRows(testDb, orgId), (row) => [row.chain_name, row.status]), + [ + ["Warn chain", "superseded"], + ["Page chain", "exhausted"], + ], + ) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("refuses a rule naming an unknown chain, and deleting a chain or destination in use", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, chains, orgId, userId, team, slack, chain, rule } = yield* setup("org_esc_guards") + const unknown = yield* Effect.exit( + alerts.createRule( + orgId, + userId, + adminRoles, + ruleRequest([ + tier("critical", 5, [team.id], decodeChainId("00000000-0000-4000-8000-0000000000ff")), + ]), + ), + ) + assert.instanceOf(getError(unknown), AlertValidationError) + + const inUse = getError(yield* Effect.exit(chains.delete(orgId, adminRoles, chain.id))) + assert.instanceOf(inUse, EscalationChainInUseError) + assert.deepStrictEqual((yield* chains.get(orgId, chain.id)).ruleIds, [rule.id]) + + const destinationInUse = getError( + yield* Effect.exit(alerts.deleteDestination(orgId, adminRoles, slack.id)), + ) + if (!Schema.is(AlertDestinationInUseError)(destinationInUse)) + return assert.fail("expected in use") + assert.include(destinationInUse.message, "API escalation") + + yield* alerts.deleteRule(orgId, adminRoles, rule.id) + yield* chains.delete(orgId, adminRoles, chain.id) + assert.lengthOf((yield* chains.list(orgId)).chains, 0) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) +}) diff --git a/packages/backend/src/services/alerts/alert-escalation-runs.ts b/packages/backend/src/services/alerts/alert-escalation-runs.ts index 5254c955a6..450e941b9a 100644 --- a/packages/backend/src/services/alerts/alert-escalation-runs.ts +++ b/packages/backend/src/services/alerts/alert-escalation-runs.ts @@ -23,7 +23,7 @@ import { EscalationChains, } from "@maple/db/tables" import type { MapleDb } from "@maple/db/client" -import { Array as Arr, Effect } from "effect" +import { Array as Arr, DateTime, Effect } from "effect" import { queuedDeliveryEventRow } from "./alert-delivery-rows" const LIVE_STATUSES = ["active", "acknowledged"] as const @@ -241,7 +241,7 @@ export const applyEscalationPlan = (db: MapleDb, input: ApplyEscalationPlanInput if (!moved) return "lost" satisfies EscalationApplyOutcome const payload = { ...row.payloadJson, - sentAt: new Date(nowMs).toISOString(), + sentAt: DateTime.formatIso(DateTime.makeUnsafe(nowMs)), escalation: { chainId: row.chainId, chainName: row.chainName, diff --git a/packages/backend/src/services/alerts/alert-thresholds.ts b/packages/backend/src/services/alerts/alert-thresholds.ts index e635d5c788..b2a76ec904 100644 --- a/packages/backend/src/services/alerts/alert-thresholds.ts +++ b/packages/backend/src/services/alerts/alert-thresholds.ts @@ -151,11 +151,16 @@ export const validateThresholds = ( ) } } - if (options.requireDestinations && tier.destinationIds.length === 0) { + // An escalation chain routes the tier on its own. + if ( + options.requireDestinations && + tier.destinationIds.length === 0 && + tier.escalationChainId == null + ) { details.push( thresholds.length === 1 - ? "at least one destination must be selected" - : `the ${tier.severity} threshold needs at least one destination`, + ? "at least one destination or an escalation chain must be selected" + : `the ${tier.severity} threshold needs at least one destination or an escalation chain`, ) } }) From 17b8598ee90c83d7d0bd05fea60bd88a8f29f4f0 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 13:07:33 +0100 Subject: [PATCH 12/29] feat(alerts): escalation chains in the v2 API and MCP, and rule tiers that name one --- apps/ai/src/mcp/dispatcher.ts | 2 + apps/ai/src/mcp/lib/alert-rules.ts | 25 ++ apps/ai/src/mcp/lib/escalation-chains.ts | 174 ++++++++++++ apps/ai/src/mcp/lib/params.ts | 6 + apps/ai/src/mcp/resources/instructions.ts | 1 + apps/ai/src/mcp/tool-descriptors.generated.ts | 14 +- .../mcp/tools/__tests__/alert-tools.test.ts | 117 ++++++++ .../__tests__/escalation-chain-tools.test.ts | 261 +++++++++++++++++ apps/ai/src/mcp/tools/create-alert-rule.ts | 28 +- .../src/mcp/tools/create-escalation-chain.ts | 76 +++++ .../src/mcp/tools/delete-escalation-chain.ts | 52 ++++ apps/ai/src/mcp/tools/get-alert-rule.ts | 28 +- .../src/mcp/tools/list-escalation-chains.ts | 64 +++++ apps/ai/src/mcp/tools/mutating.ts | 3 + apps/ai/src/mcp/tools/registry.ts | 8 + apps/ai/src/mcp/tools/runtime-requirements.ts | 2 + apps/ai/src/mcp/tools/update-alert-rule.ts | 30 +- .../src/mcp/tools/update-escalation-chain.ts | 80 ++++++ .../test/mcp/unimplemented-tool-services.ts | 2 + apps/api/src/routes/v2/alerts.http.test.ts | 174 +++++++++++- .../src/routes/v2/escalation-chains.http.ts | 141 ++++++++++ apps/api/src/routes/v2/v2-test-support.ts | 4 + apps/api/src/runtime/http-graph.ts | 2 + apps/api/src/runtime/service-graph.ts | 2 + .../MapleAPI/Sources/MapleAPI/openapi.json | 19 ++ .../landing/src/content/docs/reference/mcp.md | 4 + apps/web/e2e/tests/alert-rules.e2e.ts | 3 + apps/web/src/page/alerts/editor/form-steps.ts | 7 +- apps/web/src/page/alerts/editor/form.ts | 10 +- .../conversation/kit/tool-metadata.ts | 8 + packages/domain/src/audit-actions.ts | 8 + .../anticipated-error-identifiers.ts | 2 + packages/domain/src/http/v2/alert-rules.ts | 11 +- packages/domain/src/http/v2/api.ts | 2 + .../domain/src/http/v2/escalation-chains.ts | 264 ++++++++++++++++++ packages/domain/src/http/v2/index.ts | 1 + packages/domain/src/http/v2/openapi.test.ts | 5 + packages/domain/src/http/v2/public-id.ts | 1 + packages/domain/src/http/v2/resource-ids.ts | 2 + .../domain/src/http/v2/v2-contract.test.ts | 55 ++++ packages/domain/src/mcp-outputs/alerts.ts | 10 + packages/domain/src/mcp-outputs/catalog.ts | 9 + .../src/mcp-outputs/escalation-chains.ts | 39 +++ packages/domain/src/mcp-outputs/index.ts | 1 + .../web-data/src/alerts/form-utils.test.ts | 27 +- packages/web-data/src/alerts/form-utils.ts | 9 + 46 files changed, 1775 insertions(+), 18 deletions(-) create mode 100644 apps/ai/src/mcp/lib/escalation-chains.ts create mode 100644 apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts create mode 100644 apps/ai/src/mcp/tools/create-escalation-chain.ts create mode 100644 apps/ai/src/mcp/tools/delete-escalation-chain.ts create mode 100644 apps/ai/src/mcp/tools/list-escalation-chains.ts create mode 100644 apps/ai/src/mcp/tools/update-escalation-chain.ts create mode 100644 apps/api/src/routes/v2/escalation-chains.http.ts create mode 100644 packages/domain/src/http/v2/escalation-chains.ts create mode 100644 packages/domain/src/mcp-outputs/escalation-chains.ts diff --git a/apps/ai/src/mcp/dispatcher.ts b/apps/ai/src/mcp/dispatcher.ts index 9c07575f90..08299cc610 100644 --- a/apps/ai/src/mcp/dispatcher.ts +++ b/apps/ai/src/mcp/dispatcher.ts @@ -12,6 +12,7 @@ import { AlertsService } from "@maple/backend/services/alerts/AlertsService" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import { DashboardPersistenceService } from "@maple/backend/services/dashboards/DashboardPersistenceService" import { ErrorActorsService } from "@maple/backend/services/errors/ErrorActorsService" @@ -272,6 +273,7 @@ const McpRuntimeServicesLive = Layer.mergeAll( AlertReadModelsService.layer, AlertIncidentAckService.layer, AlertRulesService.layer, + EscalationChainsService.layer, AlertsService.layer, AuditLogService.layer, DashboardPersistenceService.layer, diff --git a/apps/ai/src/mcp/lib/alert-rules.ts b/apps/ai/src/mcp/lib/alert-rules.ts index 4943da9d11..ae0ad8ac8a 100644 --- a/apps/ai/src/mcp/lib/alert-rules.ts +++ b/apps/ai/src/mcp/lib/alert-rules.ts @@ -81,10 +81,34 @@ export const toAlertRuleRow = (rule: AlertRuleDocument): typeof AlertRuleRow.Typ evaluationIntervalMinutes: rule.evaluationIntervalMinutes, slowedIntervalMinutes: rule.slowedIntervalMinutes, destinationIds: [...rule.destinationIds], + ...escalationChainsOf(rule), createdAt: rule.createdAt, updatedAt: rule.updatedAt, }) +/** The chains a rule's tiers start, keyed by severity; absent when no tier names one. */ +const escalationChainsOf = (rule: Pick) => { + const chains = Arr.flatMap(rule.thresholds, (tier) => + tier.escalationChainId == null ? [] : [{ severity: tier.severity, chainId: tier.escalationChainId }], + ) + return chains.length === 0 ? undefined : { escalationChains: chains } +} + +/** e.g. `critical: Checkout on-call (3f2a…)`, or the bare id when the name was not looked up. */ +export const formatEscalationChains = ( + chains: (typeof AlertRuleRow.Type)["escalationChains"], +): string | undefined => + chains === undefined + ? undefined + : Arr.join( + Arr.map(chains, (c) => + c.name == null + ? `${c.severity}: ${c.chainId}` + : `${c.severity}: ${c.name} (${c.chainId})`, + ), + "; ", + ) + interface RuleCadence { readonly windowMinutes: number readonly evaluationIntervalMinutes: number | null @@ -266,6 +290,7 @@ export const renderRuleWrite = ( ["Window", `${rule.windowMinutes}m, checked ${formatCheckInterval(rule)}`], ["Enabled", rule.enabled ? "Yes" : "No"], ["Destinations", rule.destinationIds.length], + ["Escalation", formatEscalationChains(rule.escalationChains)], [ "Environments", rule.environments.length > 0 ? Arr.join(rule.environments, ", ") : undefined, diff --git a/apps/ai/src/mcp/lib/escalation-chains.ts b/apps/ai/src/mcp/lib/escalation-chains.ts new file mode 100644 index 0000000000..4bbf440b24 --- /dev/null +++ b/apps/ai/src/mcp/lib/escalation-chains.ts @@ -0,0 +1,174 @@ +/** Pieces the escalation chain MCP tools share: rows, step text, step input and error mapping. */ +import { Array as Arr, Effect, Option, Schema } from "effect" +import type { + AlertDestinationDocument, + AlertForbiddenError, + OrgId, + AlertPersistenceError, + AlertValidationError, + EscalationChainDocument, + EscalationChainInUseError, + EscalationChainNotFoundError, +} from "@maple/domain/http" +import { encodePublicId, PublicIdPrefixes } from "@maple/domain/http/v2" +import type { EscalationChainRow } from "@maple/domain/mcp-outputs" +import { AlertsService } from "@maple/backend/services/alerts/AlertsService" +import { McpInvalidInputError } from "../tools/types" +import { toMcpHttpError } from "./map-http-error" +import * as P from "./params" +import { doc } from "./tool-doc" + +type ChainRow = typeof EscalationChainRow.Type +type DestinationRef = Pick + +/** The steps a tool takes: each the destinations it notifies and the minutes it waits. */ +export const StepsInput = Schema.Array( + Schema.Struct({ + destination_ids: Schema.Array(Schema.String).annotate({ + description: "Destinations this step notifies, from list_alert_destinations (UUID or dest_…)", + }), + wait_minutes: Schema.Number.annotate({ + description: "Minutes to wait for an acknowledgement before the next step, 1 to 1440", + }), + }), +) + +export const stepsParameterDescription = + 'Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{"destination_ids":["dest_…"],"wait_minutes":10},{"destination_ids":["dest_…"],"wait_minutes":15}]. At most 10 steps of 10 destinations' + +/** The steps as the service schema takes them, destination ids decoded to internal ids. */ +export const toStepsRequest = (steps: typeof StepsInput.Type) => + Effect.forEach(steps, (step) => + Effect.map( + Effect.forEach(step.destination_ids, P.decodeResourceId("alertDestination", "steps")), + (ids) => ({ + targets: Arr.map(ids, (destinationId) => ({ type: "destination" as const, destinationId })), + waitMinutes: step.wait_minutes, + }), + ), + ) + +/** Destination names for the rendered steps; a failed read only drops the names. */ +export const destinationDirectory = (orgId: OrgId) => + Effect.gen(function* () { + const alerts = yield* AlertsService + return yield* alerts.listDestinations(orgId).pipe( + Effect.map((response): ReadonlyArray => response.destinations), + Effect.orElseSucceed((): ReadonlyArray => []), + ) + }) + +export const toEscalationChainRow = ( + chain: EscalationChainDocument, + destinations: ReadonlyArray, +): ChainRow => ({ + id: chain.id, + publicId: encodePublicId(PublicIdPrefixes.escalationChain, chain.id), + name: chain.name, + description: chain.description, + steps: Arr.map(chain.steps, (step) => ({ + targets: Arr.map(step.targets, (target) => { + const known = Option.getOrUndefined( + Arr.findFirst(destinations, (d) => d.id === target.destinationId), + ) + return { + destinationId: target.destinationId, + name: known?.name ?? null, + type: known?.type ?? null, + } + }), + waitMinutes: step.waitMinutes, + })), + repeatCount: chain.repeatCount, + ruleIds: [...chain.ruleIds], + createdAt: chain.createdAt, + updatedAt: chain.updatedAt, +}) + +const formatWait = (minutes: number): string => + minutes < 60 + ? `${minutes}m` + : minutes % 60 === 0 + ? `${minutes / 60}h` + : `${Math.floor(minutes / 60)}h ${minutes % 60}m` + +const targetLabel = (target: ChainRow["steps"][number]["targets"][number]): string => + target.name === null + ? `destination ${target.destinationId}` + : target.type === null + ? target.name + : `${target.name} (${target.type})` + +/** One line per step, e.g. `1. notify On-call (pagerduty), then wait 10m for an ack`. */ +export const formatSteps = (chain: ChainRow): string => + Arr.join( + Arr.map( + chain.steps, + (step, index) => + `${index + 1}. notify ${Arr.join(Arr.map(step.targets, targetLabel), ", ")}, then wait ${formatWait(step.waitMinutes)} for an ack`, + ), + "\n", + ) + +export const formatRepeats = (repeatCount: number): string => + repeatCount === 0 ? "runs once" : `repeats ${repeatCount} more time${repeatCount === 1 ? "" : "s"}` + +export const chainBlocks = (chain: ChainRow) => [ + doc.fields([ + ["ID", chain.id], + ["REST id", chain.publicId], + ["Name", chain.name], + ["Description", chain.description ?? undefined], + ["Passes", formatRepeats(chain.repeatCount)], + ["Used by rules", chain.ruleIds.length > 0 ? Arr.join(chain.ruleIds, ", ") : "none"], + ]), + doc.text(formatSteps(chain)), +] + +export const chainNotFound = (chainId: string) => + new McpInvalidInputError({ + message: `Escalation chain not found: ${chainId}. Use list_escalation_chains to find chain IDs.`, + parameter: "chain_id", + }) + +const failWith = (pipeName: string) => (error: AlertForbiddenError | AlertPersistenceError) => + Effect.fail(toMcpHttpError(pipeName)(error)) + +const invalidChain = (error: AlertValidationError) => + Effect.fail( + new McpInvalidInputError({ + message: `Invalid escalation chain: ${error.message}`, + parameter: "steps", + }), + ) + +export const createChainErrors = { + "@maple/http/errors/AlertValidationError": invalidChain, + "@maple/http/errors/AlertForbiddenError": failWith("create_escalation_chain"), + "@maple/http/errors/AlertPersistenceError": failWith("create_escalation_chain"), +} + +export const updateChainErrors = { + "@maple/http/errors/AlertValidationError": invalidChain, + "@maple/http/errors/EscalationChainNotFoundError": (error: EscalationChainNotFoundError) => + Effect.fail(chainNotFound(error.chainId)), + "@maple/http/errors/AlertForbiddenError": failWith("update_escalation_chain"), + "@maple/http/errors/AlertPersistenceError": failWith("update_escalation_chain"), +} + +export const deleteChainErrors = { + "@maple/http/errors/EscalationChainNotFoundError": (error: EscalationChainNotFoundError) => + Effect.fail(chainNotFound(error.chainId)), + "@maple/http/errors/EscalationChainInUseError": (error: EscalationChainInUseError) => + Effect.fail( + new McpInvalidInputError({ + message: `Escalation chain is still used by ${Arr.join( + Arr.map(error.ruleIds, (id, index) => `"${error.ruleNames[index] ?? id}" (${id})`), + ", ", + )}. Detach it first with update_alert_rule escalation_chain_id="none".`, + parameter: "chain_id", + }), + ), + "@maple/http/errors/AlertForbiddenError": failWith("delete_escalation_chain"), + "@maple/http/errors/AlertPersistenceError": failWith("delete_escalation_chain"), +} diff --git a/apps/ai/src/mcp/lib/params.ts b/apps/ai/src/mcp/lib/params.ts index 2ee14fe336..6d5b8cefe6 100644 --- a/apps/ai/src/mcp/lib/params.ts +++ b/apps/ai/src/mcp/lib/params.ts @@ -24,6 +24,7 @@ import { AlertRuleId, DashboardId, ErrorIssueId, + EscalationChainId, } from "@maple/domain/primitives" import { WarehouseTimeInput, type WarehouseDateTime } from "@maple/query-engine" import { McpInvalidInputError } from "../tools/types" @@ -157,6 +158,11 @@ const RESOURCE_IDS = { schema: AlertDestinationId, noun: "alert destination", }, + escalationChain: { + prefix: PublicIdPrefixes.escalationChain, + schema: EscalationChainId, + noun: "escalation chain", + }, dashboard: { prefix: PublicIdPrefixes.dashboard, schema: DashboardId, noun: "dashboard" }, errorIssue: { prefix: PublicIdPrefixes.errorIssue, schema: ErrorIssueId, noun: "error issue" }, } as const diff --git a/apps/ai/src/mcp/resources/instructions.ts b/apps/ai/src/mcp/resources/instructions.ts index 8c69604cab..8975b6cff8 100644 --- a/apps/ai/src/mcp/resources/instructions.ts +++ b/apps/ai/src/mcp/resources/instructions.ts @@ -117,6 +117,7 @@ first occurrences ──> (candidate) ──> issue in \`triage\` - Infrastructure (hosts, Kubernetes pods/nodes/workloads, Docker containers): list_infra (no \`kind\` = overview of what reports, hottest first) -> inspect_infra (CPU/memory over the window with the peak time, a node's or workload's pods). diagnose_service shows the workload and pods a service runs on. Connected integrations add kinds: cloudflare_zones / cloudflare_workers (requests, 5xx, latency; inspect a zone with section=traffic|breakdown|security|dns), railway_services and planetscale_databases; list_integrations says which are connected and healthy. Resource metrics are also chartable: create_dashboard group_by \`resource.host.name\` / \`resource.k8s.pod.name\` on source=metrics - Deploy verification: service_deployments (versions per service, newest vs previous) -> compare_periods around the rollout. Route usage over up to 90 days: route_usage. Silence check before calling an outage: ingest_freshness. Volume per service: ingest_usage. Database query shapes by volume: db_query_volume - Alert management: list_alert_rules / list_alert_destinations -> get_alert_rule -> preview_alert_rule (dry-run before saving; a window with no data is a blind rule, not a healthy one) -> create_alert_rule / update_alert_rule / delete_alert_rule -> list_alert_incidents +- Paging step by step until someone acknowledges: list_escalation_chains / create_escalation_chain (steps of destinations + wait_minutes) / update_escalation_chain / delete_escalation_chain; attach to a rule with create_alert_rule / update_alert_rule escalation_chain_id. The rule's own destinations still fire at once. - Uptime: list_uptime_monitors is every http/tcp/dns check and heartbeat with its status and last check; a failed check's trace id goes to inspect_trace. Admins manage them with create_uptime_monitor / update_uptime_monitor (enabled=false pauses) / delete_uptime_monitor. A down monitor opens an alert incident with signal type \`uptime\`; its rule is managed only through the monitor. - Status pages (what customers see, separate from alert incidents): list_status_pages -> get_status_page (component statuses, open incidents, freshness) -> create_status_page_incident / schedule_status_page_maintenance (drafts unless publish=true) -> post_status_page_update -> resolve_status_page_incident. Every message is public text. Admins edit a whole page (settings, theme, components) with get_status_page_config -> apply_status_page_config, one atomic write. - AI agent sessions (LLM agent traces — NOT browser replays): list_agent_sessions -> get_agent_session (verdict and checks) -> get_agent_session_transcript (what it did step by step; \`failed_only\`, \`turn\`) -> inspect_span (one span in full) diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index f99f020768..4366c1489b 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -27,17 +27,21 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"query_data","title":"Query Data","description":"Aggregate traces, logs, metrics or product events into a timeseries or a top-N breakdown. Start here for trends, comparisons and top-N; for errors prefer find_errors and error_detail; for a shape this tool cannot express, run_sql. Applied defaults are listed in the result. A traces breakdown by span_name or attribute reads raw spans: set `service` and keep the window short (a window too large is narrowed, with a note). For endpoint volume over days use route_usage.","inputSchema":{"type":"object","properties":{"source":{"type":"string","enum":["traces","logs","metrics","product_events"],"description":"'traces' for request/span analysis (latency, errors, throughput); 'logs' for log volume; 'metrics' for a custom metric (needs metric_name and metric_type from list_metrics); 'product_events' for track() events, page views and server events (names from list_product_events)."},"kind":{"type":"string","enum":["timeseries","breakdown"],"description":"'timeseries' for how a value changed over time; 'breakdown' for top-N or distribution. Pick the right one first rather than calling twice."},"metric":{"type":"string","description":"Traces: count, avg_duration, p50_duration, p95_duration, p99_duration, error_rate (0-1 ratio), apdex (needs apdex_threshold_ms). Logs: count. Product events: count, sessions, persons, users, visitors. Metrics: avg, sum, min, max, count, rate, increase. For counters (sum metrics, Prometheus *_total gauges) use increase (total over the window) or rate (per second); both are per-series and reset-aware. sum adds raw samples. Default: count, or avg for metrics."},"group_by":{"type":"string","description":"Traces: service, span_name, status_code, http_method, attribute, resource_attribute. Logs: service, severity. Metrics: service, attribute, resource_attribute. Product events: event_name, kind, source, host, page_path, service, group, attribute. 'attribute' and 'resource_attribute' need attribute_key (resource_attribute reads resource keys such as k8s.pod.name or deployment.environment). Timeseries also take 'none' (the default); breakdown defaults to service."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 6 hours before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"service":{"type":"string","description":"Only this service (exact `service.name`)"},"span_name":{"type":"string","description":"Filter by span name (traces only). Exact match first; when nothing matches exactly it is matched as a case-insensitive substring"},"root_spans_only":{"type":"boolean","description":"Only include root spans (traces only)"},"environments":{"type":"array","items":{"type":"string"},"description":"Only these deployment environments (traces, logs, metrics; explore_attributes source=services lists them)"},"commit_shas":{"type":"array","items":{"type":"string"},"description":"Commit SHAs to filter (traces only)"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in ms (traces only; needed for metric=apdex)"},"severity":{"type":"string","enum":["TRACE","DEBUG","INFO","WARN","ERROR","FATAL"],"description":"Only this log severity (logs only)"},"event_name":{"type":"array","items":{"type":"string"},"description":"Only these event names (product_events only)"},"event_kind":{"type":"array","items":{"type":"string","enum":["navigation","custom","screen"]},"description":"Only these event kinds (product_events only)"},"host":{"type":"string","description":"Only events fired on this site host (product_events only)"},"page_path":{"type":"string","description":"Only events fired on this page path (product_events only)"},"metric_name":{"type":"string","description":"The metric, from list_metrics (source=metrics only)"},"metric_type":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"],"description":"Type of `metric_name`, as list_metrics reports it"},"attribute_key":{"type":"string","description":"Attribute to filter on, or to group by with group_by=attribute. Span (or log/metric label) and resource attributes both work; the scope is resolved automatically"},"attribute_value":{"type":"string","description":"Exact value for attribute_key. Also applies when grouping by the same key"},"attribute_scope":{"type":"string","enum":["span","resource"],"description":"Where attribute_key lives: 'span' (span, log or metric-datapoint attributes) or 'resource' (deployment.environment, k8s.pod.name...). Resolved from the known keys when omitted"},"bucket_seconds":{"type":"number","description":"Bucket width in seconds (timeseries only; auto-computed when omitted)"},"limit":{"type":"number","description":"Max rows of a breakdown (default 10, max 100)"}},"required":["source","kind"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"kind":{"type":"string"},"metric":{"type":"string"},"groupBy":{"type":"string"},"decisions":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}},"queryContext":{"type":"object","properties":{"source":{"type":"string","enum":["traces","logs","metrics","product_events"]},"serviceName":{"type":"string"},"eventName":{"type":"string"},"eventKind":{"type":"string"},"host":{"type":"string"},"pagePath":{"type":"string"},"spanName":{"type":"string"},"rootSpansOnly":{"type":"boolean"},"environments":{"type":"array","items":{"type":"string"}},"commitShas":{"type":"array","items":{"type":"string"}},"severity":{"type":"string"},"metricName":{"type":"string"},"metricType":{"type":"string"},"attributeFilters":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"},"mode":{"type":"string"}},"required":["key","mode"],"additionalProperties":true}},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"bucketSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["source"],"additionalProperties":true},"unit":{"type":"string","enum":["duration_ms","duration_us","duration_s","duration_ns","percent","number","bytes","requests_per_sec"]},"result":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["timeseries"]},"data":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string"},"series":{"type":"object","additionalProperties":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["bucket","series"],"additionalProperties":true}}},"required":["kind","data"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["breakdown"]},"data":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["name","value"],"additionalProperties":true}}},"required":["kind","data"],"additionalProperties":true}]},"emptyHints":{"type":"array","items":{"type":"string"}}},"required":["timeRange","kind","metric","queryContext","unit","result"],"additionalProperties":true},"annotations":{"title":"Query Data","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"run_sql","title":"Run SQL","description":"Run one read-only ClickHouse SELECT against the org's warehouse and return its rows: at most 1000 are fetched and the first 100 rendered, with the count reported. Use it for what query_data cannot express, to spot-check data, or to test SQL for a raw_sql widget. For trends and top-N prefer query_data. The SQL must AND in `$__orgFilter`; macros are listed on `sql`. Spans are in `traces` (no otel_spans/spans table), log lines in `logs`; also metrics_sum/metrics_gauge/metrics_histogram, error_events, session_replays, product_events and *_hourly/*_minutely rollups. Select 64-bit IDs and hashes as toString(col). Attribute columns are Map(String, String): SpanAttributes['http.route'], never JSON* functions. In traces the name is SpanName, StatusCode is 'Ok'/'Error' and Duration is nanoseconds. Compare timestamps to 'YYYY-MM-DD HH:MM:SS', not ISO T/Z strings; bucket with toStartOfInterval. Call describe_warehouse_tables for a table's columns, units and enum casing before guessing names.","inputSchema":{"type":"object","properties":{"sql":{"type":"string","description":"The SELECT. It MUST contain `$__orgFilter` (expands to `(OrgId = '')`) as a top-level AND condition; a query without it, or with it OR-ed against another condition, is rejected. In joins use `$__orgFilter(alias)` (`(alias.OrgId = '')`) once per joined table. Other macros: `$__timeFilter(Column)` (`Column >= AND Column <= `), `$__startTime`, `$__endTime`, `$__interval_s` (bucket width for toStartOfInterval). Only one statement; DDL/DML, SETTINGS and system tables are rejected."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 6 hours before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"granularity_seconds":{"type":"number","description":"Value of `$__interval_s`. Auto-computed from the window when omitted."},"max_cell_chars":{"type":"number","description":"Clip each rendered cell to this many characters (default 120, max 2000). Raise it to read long values such as log bodies or stack traces."},"show_sql":{"type":"boolean","description":"Also return the macro-expanded SQL that ran (default false)."},"reason":{"type":"string","description":"Why you are running this query, in a sentence. Optional; does not change the query."}},"required":["sql"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rowCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"rows":{"type":"array","items":{"type":"object"}},"truncated":{"type":"boolean"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"maxCellChars":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowApplied":{"type":"boolean"},"expandedSql":{"type":"string"},"atRowCap":{"type":"boolean"},"rewrites":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"additionalProperties":true}}},"required":["rowCount","columns","rows","truncated","timeRange"],"additionalProperties":true},"annotations":{"title":"Run SQL","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"service_map","title":"Service Map","description":"Show service-to-service dependencies with call counts, error rates, and latency per edge. Use to understand system architecture and identify problematic inter-service calls.","inputSchema":{"type":"object","properties":{"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 6 hours before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"service":{"type":"string","description":"Only edges involving this service, as source or target (exact `service.name`)"},"environment":{"type":"string","description":"Only this deployment environment (e.g. production, staging)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"edges":{"type":"array","items":{"type":"object","properties":{"sourceService":{"type":"string"},"targetService":{"type":"string"},"callCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errorCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"avgDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"maxDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["sourceService","targetService","callCount","errorCount","avgDurationMs","maxDurationMs"],"additionalProperties":true}},"serviceCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"service":{"type":"string"},"environment":{"type":"string"}},"required":["timeRange","edges","serviceCount"],"additionalProperties":true},"annotations":{"title":"Service Map","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"list_alert_rules","title":"List Alert Rules","description":"List configured alert rules with severity, signal type and condition. Use list_alert_incidents for what has fired.","inputSchema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"string"},"description":"Only rules covering any of these services: scoped to them, scoped to every service, or raw_query rules whose SQL or name mentions them"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"Only rules on this signal type"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only rules with this severity"},"enabled_only":{"type":"boolean","description":"Only enabled rules"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"services":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"severity":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["rules","total"],"additionalProperties":true},"annotations":{"title":"List Alert Rules","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"list_alert_rules","title":"List Alert Rules","description":"List configured alert rules with severity, signal type and condition. Use list_alert_incidents for what has fired.","inputSchema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"string"},"description":"Only rules covering any of these services: scoped to them, scoped to every service, or raw_query rules whose SQL or name mentions them"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"Only rules on this signal type"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only rules with this severity"},"enabled_only":{"type":"boolean","description":"Only enabled rules"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"services":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"severity":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["rules","total"],"additionalProperties":true},"annotations":{"title":"List Alert Rules","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_destinations","title":"List Alert Destinations","description":"List the notification destinations a rule can deliver to, with id, type and delivery health. Pass the ids as destination_ids to create_alert_rule / update_alert_rule.","inputSchema":{"type":"object","properties":{"type":{"type":"string","enum":["pagerduty","webhook","discord","telegram","google-chat","microsoft-teams","incident-io","email","chat","linear"],"description":"Only destinations of this type"},"enabled_only":{"type":"boolean","description":"Only enabled destinations"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"enabled":{"type":"boolean"},"summary":{"type":"string"},"channelLabel":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestError":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabledReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","type","enabled","summary","channelLabel","lastTestedAt","lastTestError","disabledReason","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"type":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["destinations","total"],"additionalProperties":true},"annotations":{"title":"List Alert Destinations","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"get_alert_rule","title":"Get Alert Rule","description":"Get full configuration details of a specific alert rule including thresholds, service filters, evaluation settings, and notification destinations. Use list_alert_rules to find rule IDs.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"excludeServiceNames":{"type":"array","items":{"type":"string"}},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noDataBehavior":{"type":"string"},"consecutiveBreachesRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthyRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"renotifyIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"queryBuilderDraft":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"null"}]},"rawQuerySql":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawQueryReducer":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"notificationTitle":{"anyOf":[{"type":"string"},{"type":"null"}]},"notificationBody":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationError":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationErrorKind":{"anyOf":[{"type":"string"},{"type":"null"}]},"slowedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}},"required":["id","name","type","enabled"],"additionalProperties":true}}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt","excludeServiceNames","groupBy","minimumSampleCount","noDataBehavior","consecutiveBreachesRequired","consecutiveHealthyRequired","renotifyIntervalMinutes","apdexThresholdMs","queryBuilderDraft","rawQuerySql","rawQueryReducer"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Get Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"get_alert_rule","title":"Get Alert Rule","description":"Get full configuration details of a specific alert rule including thresholds, service filters, evaluation settings, and notification destinations. Use list_alert_rules to find rule IDs.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"excludeServiceNames":{"type":"array","items":{"type":"string"}},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noDataBehavior":{"type":"string"},"consecutiveBreachesRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthyRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"renotifyIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"queryBuilderDraft":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"null"}]},"rawQuerySql":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawQueryReducer":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"notificationTitle":{"anyOf":[{"type":"string"},{"type":"null"}]},"notificationBody":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationError":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationErrorKind":{"anyOf":[{"type":"string"},{"type":"null"}]},"slowedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}},"required":["id","name","type","enabled"],"additionalProperties":true}}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt","excludeServiceNames","groupBy","minimumSampleCount","noDataBehavior","consecutiveBreachesRequired","consecutiveHealthyRequired","renotifyIntervalMinutes","apdexThresholdMs","queryBuilderDraft","rawQuerySql","rawQueryReducer"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Get Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_incidents","title":"List Alert Incidents","description":"Alert incidents: alert-rule breaches, open and resolved, with severity, group, condition and last observed value (flare-ups of an error issue are list_error_incidents). For one rule's trigger, notify and resolve timestamps use get_incident_timeline.","inputSchema":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"acknowledged":{"type":"boolean","description":"true: open incidents someone acknowledged; false: open incidents nobody has. Implies status=open"},"limit":{"type":"number","description":"Max incidents to return (default 50, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"holdReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"heldSince":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"firstTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","holdReason","heldSince","threshold","comparator","firstTriggeredAt","resolvedAt","lastObservedValue"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"recentlyResolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"List Alert Incidents","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_checks","title":"List Alert Checks","description":"List a rule's recent checks (one row per evaluation) with observed value, threshold, sample count and incident transition. Use it to tune thresholds, diagnose flapping, or see the near-misses before a breach.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"group_key":{"type":"string","description":"Only checks for this exact group key"},"status":{"type":"string","enum":["breached","healthy","skipped","error"],"description":"Only checks with this status"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"limit":{"type":"number","description":"Max checks to return (default 100, max 2000)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"skipped":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errored":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transitions":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"checks":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"string"},"groupKey":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"observedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowStart":{"type":"string"},"windowEnd":{"type":"string"},"consecutiveBreaches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"incidentId":{"anyOf":[{"type":"string"},{"type":"null"}]},"incidentTransition":{"type":"string"},"evaluationDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errorMessage":{"anyOf":[{"type":"string"},{"type":"null"}]},"errorCategory":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["timestamp","groupKey","status","skipReason","observedValue","threshold","comparator","sampleCount","windowStart","windowEnd","consecutiveBreaches","consecutiveHealthy","incidentId","incidentTransition","evaluationDurationMs","errorMessage","errorCategory"],"additionalProperties":true}},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"groupKey":{"type":"string"},"status":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["ruleId","total","breached","healthy","skipped","noData","belowMinSamples","errored","transitions","checks"],"additionalProperties":true},"annotations":{"title":"List Alert Checks","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations).","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, - {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations). escalation_chain_id adds step-by-step paging on top.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, + {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or \"none\" to detach it. On a rule with warning and critical tiers it sets the critical tier: its UUID or its REST id (esc_\u2026)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_alert_rule","title":"Delete Alert Rule","description":"Permanently delete an alert rule together with its incident history, delivery events and evaluation state. Irreversible; requires confirm=true. Ids from list_alert_rules.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"confirm":{"type":"boolean","description":"Must be true. There is no undo."}},"required":["rule_id","confirm"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"annotations":{"title":"Delete Alert Rule","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleId","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, + {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id=\"none\" first. Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_dashboard","title":"Get Dashboard","description":"Full configuration of one dashboard: every widget with its id, visualization, dataSource, display, layout and any pinned timeRange. Widgets come back in the shape update_dashboard_widget and replace_dashboard_widgets take. Ids from list_dashboards.","inputSchema":{"type":"object","properties":{"dashboard_id":{"type":"string","description":"Dashboard ID: its UUID or its REST id (dash_\u2026)"}},"required":["dashboard_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"publicId":{"type":"string"},"dashboard":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"widgets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"visualization":{"type":"string","enum":["chart","hbar","stat","gauge","table","list","pie","histogram","heatmap","funnel","paths","markdown"]},"dataSource":{"$ref":"#/$defs/@maple~1WidgetDataSource"},"display":{"type":"object","properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartPresentation":{"anyOf":[{"type":"object","properties":{"legend":{"anyOf":[{"type":"string","enum":["visible","hidden","right"]},{"type":"null"}]},"seriesStats":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tooltip":{"anyOf":[{"type":"string","enum":["visible","hidden"]},{"type":"null"}]},"showPoints":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fillNulls":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"boolean","enum":[false]},{"type":"null"}]},"compareToPreviousPeriod":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"xAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"yAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMin":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMax":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScale":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fitYAxisToData":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"seriesMapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"colorOverrides":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"stacked":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"curveType":{"anyOf":[{"type":"string","enum":["linear","monotone"]},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}]},"sparkline":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"dataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1WidgetDataSource"},{"type":"null"}]}},"required":["enabled"],"additionalProperties":true},{"type":"null"}]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"header":{"type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"align":{"anyOf":[{"type":"string","enum":["left","center","right"]},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]}},"required":["field","header"],"additionalProperties":true}},{"type":"null"}]},"listDataSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"listWhereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"listLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"listRootOnly":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"pie":{"anyOf":[{"type":"object","properties":{"donut":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"innerRadius":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"showLabels":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"showPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"funnel":{"anyOf":[{"type":"object","properties":{"showStepPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["session"]},"dimension":{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},"value":{"type":"string"}},"required":["kind","dimension","value"],"additionalProperties":true}]}},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"breakdownBy":{"anyOf":[{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},{"type":"string","pattern":"^attribute:[\\s\\S]*?$"},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","enum":["bars","dropoff"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"paths":{"anyOf":[{"type":"object","properties":{"anchor":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true}]},"direction":{"anyOf":[{"type":"string","enum":["after","before"]},{"type":"null"}]},"depth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"branches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"include":{"anyOf":[{"type":"string","enum":["all","events","pages"]},{"type":"null"}]},"exclude":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]}},"required":["anchor"],"additionalProperties":true},{"type":"null"}]},"histogram":{"anyOf":[{"type":"object","properties":{"bucketCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"bucketWidth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScaleY":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"heatmap":{"anyOf":[{"type":"object","properties":{"colorScale":{"anyOf":[{"type":"string","enum":["amber","blues","reds","viridis","magma","cividis"]},{"type":"null"}]},"scaleType":{"anyOf":[{"type":"string","enum":["linear","log"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"gauge":{"anyOf":[{"type":"object","properties":{"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"style":{"anyOf":[{"type":"string","enum":["radial","bar"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"markdown":{"anyOf":[{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"layout":{"type":"object","properties":{"x":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"y":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"w":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"h":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["x","y","w","h"],"additionalProperties":true},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"sectionId":{"type":"string"},"tabId":{"type":"string"}},"required":["id","visualization","dataSource","display","layout"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","tags","timeRange","widgets","createdAt","updatedAt"],"additionalProperties":true}},"required":["publicId","dashboard"],"additionalProperties":true,"$defs":{"@maple/IsoDateTimeString":{"type":"string"},"@maple/QueryWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["query"]},"resultShape":{"type":"string","enum":["timeseries","breakdown","list"]},"queries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true}]}},"formulas":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expression":{"type":"string"},"legend":{"type":"string"},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","expression","legend"],"additionalProperties":true}},{"type":"null"}]},"comparison":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"type":"string","enum":["none","previous_period"]},{"type":"null"}]},"includePercentChange":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"defaultLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","resultShape","queries"],"additionalProperties":true,"title":"Query widget data source"},"@maple/RawSqlWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["raw_sql"]},"sql":{"type":"string"},"displayType":{"type":"string"},"granularitySeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","sql"],"additionalProperties":true,"title":"Raw SQL widget data source"},"@maple/RouteWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["route"]},"endpoint":{"type":"string"},"params":{"type":"object"},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","endpoint"],"additionalProperties":true,"title":"Route widget data source"},"@maple/StaticWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["static"]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind"],"additionalProperties":true,"title":"Static widget data source"},"@maple/WidgetDataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1QueryWidgetDataSource"},{"$ref":"#/$defs/@maple~1RawSqlWidgetDataSource"},{"$ref":"#/$defs/@maple~1RouteWidgetDataSource"},{"$ref":"#/$defs/@maple~1StaticWidgetDataSource"}],"title":"Widget data source"}}},"annotations":{"title":"Get Dashboard","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts b/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts index 2d446f0d4e..d7a0f6039e 100644 --- a/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts +++ b/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts @@ -15,9 +15,12 @@ import { AlertRulesListResponse, AlertRuleUpsertRequest, AlertValidationError, + EscalationChainDocument, + EscalationChainsListResponse, OrgId, UserId, } from "@maple/domain/http" +import { encodePublicId } from "@maple/domain/http/v2" import { CreateAlertRuleOutput, GetAlertRuleOutput, @@ -31,6 +34,7 @@ import { import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" import { AlertsService } from "@maple/backend/services/alerts/AlertsService" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import { CurrentMcpTenant } from "../../lib/query-warehouse" import { executeRegisteredMcpToolUnscoped } from "../registry" import { buildUpdatedRequest } from "../update-alert-rule" @@ -86,6 +90,7 @@ const rule = Schema.decodeSync(AlertRuleDocument)({ updatedBy: "user_tools", }) +const CHAIN_ID = "3f2a1b4c-5d6e-4f70-8a9b-0c1d2e3f4a5b" const ALL_ID = "8a1c2d3e-4b5a-4c6d-8e9f-0a1b2c3d4e5f" const RAW_ID = "9b1c2d3e-4b5a-4c6d-8e9f-0a1b2c3d4e5f" const OTHER_RAW_ID = "ab1c2d3e-4b5a-4c6d-8e9f-0a1b2c3d4e5f" @@ -283,6 +288,30 @@ const layer = (seen: Seen) => }), ), } as never), + Layer.mock(EscalationChainsService)({ + list: () => + Effect.succeed( + new EscalationChainsListResponse({ + chains: [ + Schema.decodeSync(EscalationChainDocument)({ + id: CHAIN_ID, + name: "Checkout on-call", + description: null, + steps: [ + { + targets: [{ type: "destination", destinationId: DEST_ID }], + waitMinutes: 10, + }, + ], + repeatCount: 0, + ruleIds: [RULE_ID], + createdAt: NOW, + updatedAt: NOW, + }), + ], + }), + ), + }), Layer.succeed(AlertReadModelsService, { listIncidents: (_org: unknown, options: { readonly status?: string }) => { seen.incidentOptions ??= options @@ -975,4 +1004,92 @@ describe("alert tools", () => { // The one-check timing still says which interval fits. expect(text(heavy)).toContain("One check took 7.8s") }) + + it("create_alert_rule sends an escalation chain on the rule's single tier", async () => { + const seen: Seen = {} + await ok( + "create_alert_rule", + { + name: "Errors", + template: "high_error_rate", + severity: "critical", + destination_ids: [DEST_ID], + escalation_chain_id: encodePublicId("esc", CHAIN_ID), + }, + seen, + ) + expect(seen.created?.thresholds).toEqual([ + { + severity: "critical", + value: 0.05, + upper: null, + recoveryValue: null, + destinationIds: [DEST_ID], + escalationChainId: CHAIN_ID, + }, + ]) + + const bad = await run("create_alert_rule", { + name: "Errors", + template: "high_error_rate", + destination_ids: [DEST_ID], + escalation_chain_id: encodePublicId("dest", CHAIN_ID), + }) + expect(bad._tag === "Failure" ? invalidParameter(bad.failure) : undefined).toBe("escalation_chain_id") + }) + + it("update_alert_rule sets, keeps and detaches a tier's escalation chain", () => { + const chainOf = (current: AlertRuleDocument, params: Parameters[1]) => + Arr.map( + Effect.runSync(buildUpdatedRequest(current, params)).thresholds, + (tier) => tier.escalationChainId, + ) + expect(chainOf(rule, { escalation_chain_id: CHAIN_ID })).toEqual([CHAIN_ID]) + + const chained = Schema.decodeSync(AlertRuleDocument)({ + ...rule, + thresholds: [{ ...rule.thresholds[0], escalationChainId: CHAIN_ID }], + }) + expect(chainOf(chained, { threshold: 0.2 })).toEqual([CHAIN_ID]) + expect(chainOf(chained, { escalation_chain_id: "none" })).toEqual([null]) + + const tiered = Schema.decodeSync(AlertRuleDocument)({ + ...rule, + thresholds: [ + { + severity: "warning", + value: 0.02, + upper: null, + recoveryValue: null, + destinationIds: [DEST_ID], + }, + { + severity: "critical", + value: 0.05, + upper: null, + recoveryValue: null, + destinationIds: [DEST_ID], + }, + ], + }) + expect(chainOf(tiered, { escalation_chain_id: CHAIN_ID })).toEqual([undefined, CHAIN_ID]) + + const malformed = Effect.runSync( + Effect.flip(buildUpdatedRequest(rule, { escalation_chain_id: "not-a-chain" })), + ) + expect(malformed.parameter).toBe("escalation_chain_id") + }) + + it("get_alert_rule names the escalation chain a tier starts", async () => { + const chained = Schema.decodeSync(AlertRuleDocument)({ + ...rule, + thresholds: [{ ...rule.thresholds[0], escalationChainId: CHAIN_ID }], + }) + const result = await ok("get_alert_rule", { rule_id: RULE_ID }, { rules: [chained] }) + const output = Schema.decodeUnknownSync(GetAlertRuleOutput)(result.structuredContent) + expect(output.rule.escalationChains).toEqual([ + { severity: "critical", chainId: CHAIN_ID, name: "Checkout on-call" }, + ]) + expect(text(result)).toContain(`Escalation: critical: Checkout on-call (${CHAIN_ID})`) + }) }) diff --git a/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts b/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts new file mode 100644 index 0000000000..fc8168b302 --- /dev/null +++ b/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts @@ -0,0 +1,261 @@ +/** + * The escalation chain tools through the registry: step input decoded to the service request, + * steps rendered with destination names, and service failures as input errors on the parameter. + */ +import { describe, expect, it } from "vitest" +import { Context, Effect, Layer, Schema, Array as Arr } from "effect" +import { + AlertDestinationsListResponse, + AlertValidationError, + EscalationChainDocument, + EscalationChainId, + EscalationChainInUseError, + EscalationChainNotFoundError, + EscalationChainsListResponse, + OrgId, + UserId, + type EscalationChainCreateRequest, + type EscalationChainUpdateRequest, +} from "@maple/domain/http" +import { encodePublicId } from "@maple/domain/http/v2" +import { ListEscalationChainsOutput } from "@maple/domain/mcp-outputs" +import { AlertsService } from "@maple/backend/services/alerts/AlertsService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" +import { CurrentMcpTenant } from "../../lib/query-warehouse" +import { executeRegisteredMcpToolUnscoped } from "../registry" +import type { McpToolResult } from "../types" + +const CHAIN_ID = "3f2a1b4c-5d6e-4f70-8a9b-0c1d2e3f4a5b" +const MISSING_ID = "9b8c7d6e-5f4a-4b3c-9d2e-1f0a9b8c7d6e" +const RULE_ID = "7f1c2d3e-4b5a-4c6d-8e9f-0a1b2c3d4e5f" +const PRIMARY = "0e1d2c3b-4a59-4687-9a0b-1c2d3e4f5a6b" +const SECONDARY = "1f2e3d4c-5b6a-4798-8a0b-2c3d4e5f6a7b" +const NOW = "2026-11-06T10:00:00.000Z" + +const chainDoc = (overrides: Partial = {}) => + Schema.decodeSync(EscalationChainDocument)({ + id: CHAIN_ID, + name: "Checkout on-call", + description: null, + steps: [ + { targets: [{ type: "destination", destinationId: PRIMARY }], waitMinutes: 10 }, + { targets: [{ type: "destination", destinationId: SECONDARY }], waitMinutes: 90 }, + ], + repeatCount: 1, + ruleIds: [RULE_ID], + createdAt: NOW, + updatedAt: NOW, + ...overrides, + }) + +const notFound = (id: string) => + new EscalationChainNotFoundError({ + message: "Escalation chain not found", + chainId: Schema.decodeSync(EscalationChainId)(id), + }) + +interface Seen { + created?: EscalationChainCreateRequest + updated?: EscalationChainUpdateRequest + deleted?: string + /** The service refuses creates as an unknown destination. */ + rejectCreate?: boolean + /** The chain is still named by a rule. */ + inUse?: boolean +} + +const layer = (seen: Seen) => + Layer.mergeAll( + Layer.succeed(CurrentMcpTenant, { + orgId: Schema.decodeSync(OrgId)("org_tools"), + userId: Schema.decodeSync(UserId)("user_tools"), + roles: [], + authMode: "self_hosted", + }), + Layer.mock(EscalationChainsService)({ + list: () => Effect.succeed(new EscalationChainsListResponse({ chains: [chainDoc()] })), + get: (_org, id) => (id === CHAIN_ID ? Effect.succeed(chainDoc()) : Effect.fail(notFound(id))), + create: (_org, _user, _roles, request) => { + seen.created = request + if (seen.rejectCreate === true) { + const message = "An escalation step names a destination that does not exist" + return Effect.fail(new AlertValidationError({ message, details: [message] })) + } + return Effect.succeed( + chainDoc({ + name: request.name, + steps: request.steps, + repeatCount: request.repeatCount ?? 0, + ruleIds: [], + }), + ) + }, + update: (_org, _user, _roles, id, request) => { + seen.updated = request + return id === CHAIN_ID + ? Effect.succeed(chainDoc(request.name === undefined ? {} : { name: request.name })) + : Effect.fail(notFound(id)) + }, + delete: (_org, _roles, id) => { + seen.deleted = id + return seen.inUse === true + ? Effect.fail( + new EscalationChainInUseError({ + message: "in use", + chainId: Schema.decodeSync(EscalationChainId)(id), + ruleIds: chainDoc().ruleIds, + ruleNames: ["Checkout errors"], + }), + ) + : Effect.void + }, + }), + Layer.mock(AlertsService)({ + listDestinations: () => + Effect.succeed( + Schema.decodeSync(AlertDestinationsListResponse)({ + destinations: Arr.map( + [ + [PRIMARY, "Primary pager", "pagerduty"], + [SECONDARY, "Ops channel", "webhook"], + ] as const, + ([id, name, type]) => ({ + id, + name, + type, + enabled: true, + summary: name, + channelLabel: null, + memberUserIds: null, + lastTestedAt: null, + lastTestError: null, + createdAt: NOW, + updatedAt: NOW, + }), + ), + }), + ), + }), + ) + +// The registry's requirement union names every tool's services; these tools reach only the +// ones `layer` provides, so the rest are left absent (a defect if ever reached). +const withoutUnreachedServices = (effect: Effect.Effect): Effect.Effect => + Effect.provide(effect, Context.makeUnsafe(new Map())) + +const run = (name: string, params: unknown, seen: Seen) => + Effect.runPromise( + executeRegisteredMcpToolUnscoped(name, params, "mcp").pipe( + Effect.provide(layer(seen)), + Effect.result, + withoutUnreachedServices, + ), + ) + +const ok = async (name: string, params: unknown, seen: Seen = {}) => { + const result = await run(name, params, seen) + if (result._tag !== "Success") throw new Error(`${name} failed: ${JSON.stringify(result.failure)}`) + return result.success +} + +const invalidParameter = (result: Awaited>) => + result._tag === "Failure" && "parameter" in result.failure ? result.failure.parameter : undefined + +const text = (result: McpToolResult) => result.content[0]?.text ?? "" + +describe("escalation chain tools", () => { + it("list_escalation_chains renders each step with its destinations and wait", async () => { + const result = await ok("list_escalation_chains", {}) + const output = Schema.decodeUnknownSync(ListEscalationChainsOutput)(result.structuredContent) + expect(output.chains[0]?.publicId).toBe(encodePublicId("esc", CHAIN_ID)) + expect(output.chains[0]?.steps[1]?.targets[0]?.name).toBe("Ops channel") + expect(text(result)).toContain("1. notify Primary pager (pagerduty), then wait 10m for an ack") + expect(text(result)).toContain("2. notify Ops channel (webhook), then wait 1h 30m for an ack") + expect(text(result)).toContain("repeats 1 more time") + }) + + it("create_escalation_chain decodes REST destination ids and JSON steps into the request", async () => { + const seen: Seen = {} + const result = await ok( + "create_escalation_chain", + { + name: "Checkout on-call", + steps: JSON.stringify([ + { destination_ids: [encodePublicId("dest", PRIMARY)], wait_minutes: 5 }, + { destination_ids: [SECONDARY, PRIMARY], wait_minutes: 15 }, + ]), + repeat_count: 2, + }, + seen, + ) + expect(seen.created?.steps).toEqual([ + { targets: [{ type: "destination", destinationId: PRIMARY }], waitMinutes: 5 }, + { + targets: [ + { type: "destination", destinationId: SECONDARY }, + { type: "destination", destinationId: PRIMARY }, + ], + waitMinutes: 15, + }, + ]) + expect(seen.created?.repeatCount).toBe(2) + expect(text(result)).toContain( + "2. notify Ops channel (webhook), Primary pager (pagerduty), then wait 15m", + ) + }) + + it("create_escalation_chain refuses bad steps before the service and maps an unknown destination to steps", async () => { + const seen: Seen = {} + const empty = await run("create_escalation_chain", { name: "Empty", steps: [] }, seen) + const tooLong = await run( + "create_escalation_chain", + { name: "Slow", steps: [{ destination_ids: [PRIMARY], wait_minutes: 2000 }] }, + seen, + ) + const badId = await run( + "create_escalation_chain", + { name: "Bad", steps: [{ destination_ids: ["alrt_nope"], wait_minutes: 5 }] }, + seen, + ) + for (const result of [empty, tooLong, badId]) expect(invalidParameter(result)).toBe("steps") + expect(seen.created).toBeUndefined() + + const rejected = await run( + "create_escalation_chain", + { name: "Ghost", steps: [{ destination_ids: [MISSING_ID], wait_minutes: 5 }] }, + { rejectCreate: true }, + ) + expect(invalidParameter(rejected)).toBe("steps") + }) + + it("update_escalation_chain sends only the given fields and needs at least one", async () => { + const seen: Seen = {} + await ok( + "update_escalation_chain", + { chain_id: encodePublicId("esc", CHAIN_ID), name: "Renamed" }, + seen, + ) + expect(seen.updated).toEqual({ name: "Renamed" }) + + const nothing = await run("update_escalation_chain", { chain_id: CHAIN_ID }, {}) + expect(invalidParameter(nothing)).toBe("steps") + const missing = await run("update_escalation_chain", { chain_id: MISSING_ID, repeat_count: 0 }, {}) + expect(invalidParameter(missing)).toBe("chain_id") + }) + + it("delete_escalation_chain names the rules that still use a chain", async () => { + const seen: Seen = {} + const result = await ok("delete_escalation_chain", { chain_id: CHAIN_ID }, seen) + expect(seen.deleted).toBe(CHAIN_ID) + expect(text(result)).toContain("Checkout on-call") + + const inUse = await run("delete_escalation_chain", { chain_id: CHAIN_ID }, { inUse: true }) + expect(invalidParameter(inUse)).toBe("chain_id") + expect(inUse._tag === "Failure" ? inUse.failure.message : "").toContain( + `"Checkout errors" (${RULE_ID})`, + ) + + const missing = await run("delete_escalation_chain", { chain_id: MISSING_ID }, {}) + expect(invalidParameter(missing)).toBe("chain_id") + }) +}) diff --git a/apps/ai/src/mcp/tools/create-alert-rule.ts b/apps/ai/src/mcp/tools/create-alert-rule.ts index e46bf748e7..83ef3b4031 100644 --- a/apps/ai/src/mcp/tools/create-alert-rule.ts +++ b/apps/ai/src/mcp/tools/create-alert-rule.ts @@ -142,6 +142,10 @@ export const CreateAlertRuleParameters = Schema.Struct({ "Custom notification body template (Markdown). Supports {{ variable }} substitution and " + '{{#if key}}…{{/if}} blocks, e.g. "*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}". Omit for the built-in format.', ), + escalation_chain_id: P.optionalResourceId( + "escalationChain", + "Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains)", + ), }) const invalid = (message: string, parameter: string, example?: string) => @@ -229,10 +233,16 @@ export const buildAlertRuleRequest = Effect.fnUntraced(function* ( // service scope are mutually exclusive, so an inherited groupBy must step aside // for a caller-supplied service; an explicit group_by is left to be rejected on its own terms. const effectiveGroupBy = groupBy ?? (services === undefined ? template?.groupBy : undefined) + const severity = params.severity ?? "warning" + // A chain lives on a tier, so a rule with one is sent as its single tier. + const escalationChainId = + params.escalation_chain_id === undefined + ? undefined + : yield* P.decodeResourceId("escalationChain", "escalation_chain_id")(params.escalation_chain_id) return { name: params.name, - severity: params.severity ?? "warning", + severity, signalType, comparator, threshold, @@ -241,6 +251,20 @@ export const buildAlertRuleRequest = Effect.fnUntraced(function* ( ? undefined : { evaluationIntervalMinutes: params.evaluation_interval_minutes }), destinationIds: params.destination_ids, + ...(escalationChainId === undefined + ? undefined + : { + thresholds: [ + { + severity, + value: threshold, + upper: null, + recoveryValue: null, + destinationIds: params.destination_ids, + escalationChainId, + }, + ], + }), ...(params.enabled === undefined ? undefined : { enabled: params.enabled }), ...(services === undefined ? undefined : { serviceNames: services }), ...(params.environments === undefined || params.environments.length === 0 @@ -292,7 +316,7 @@ export function registerCreateAlertRuleTool(server: McpToolRegistrar) { // repeating them here cost tokens twice for one fact. description: "Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. " + - "destination_ids is required (at least one, from list_alert_destinations).", + "destination_ids is required (at least one, from list_alert_destinations). escalation_chain_id adds step-by-step paging on top.", parameters: CreateAlertRuleParameters, aliases: { service_names: "services" }, output: CreateAlertRuleOutput, diff --git a/apps/ai/src/mcp/tools/create-escalation-chain.ts b/apps/ai/src/mcp/tools/create-escalation-chain.ts new file mode 100644 index 0000000000..f1742a689d --- /dev/null +++ b/apps/ai/src/mcp/tools/create-escalation-chain.ts @@ -0,0 +1,76 @@ +import { McpInvalidInputError, type McpToolRegistrar } from "./types" +import { Effect, Schema } from "effect" +import { EscalationChainCreateRequest } from "@maple/domain/http" +import { EscalationChainMutationOutput } from "@maple/domain/mcp-outputs" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" +import { CurrentMcpTenant } from "../lib/query-warehouse" +import { + chainBlocks, + createChainErrors, + destinationDirectory, + StepsInput, + stepsParameterDescription, + toEscalationChainRow, + toStepsRequest, +} from "../lib/escalation-chains" +import * as P from "../lib/params" +import { doc } from "../lib/tool-doc" + +const decodeCreateRequest = Schema.decodeUnknownEffect(EscalationChainCreateRequest) + +export function registerCreateEscalationChainTool(server: McpToolRegistrar) { + server.define({ + name: "create_escalation_chain", + title: "Create Escalation Chain", + description: + "Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. " + + "A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. " + + "Attach it with create_alert_rule / update_alert_rule escalation_chain_id.", + parameters: Schema.Struct({ + name: P.text("Chain name, e.g. 'Checkout on-call'"), + steps: P.json(StepsInput, stepsParameterDescription), + repeat_count: P.optionalNumber( + "Extra passes over the whole chain after the first, 0 to 5 (default 0)", + ), + description: P.optionalText("Free-form notes"), + }), + output: EscalationChainMutationOutput, + hints: { readOnly: false, destructive: false, idempotent: false }, + phrases: ["Creating an escalation chain"], + handler: Effect.fn("McpTool.createEscalationChain")(function* (params) { + const steps = yield* toStepsRequest(params.steps) + const request = yield* decodeCreateRequest({ + name: params.name, + steps, + ...(params.repeat_count === undefined ? undefined : { repeatCount: params.repeat_count }), + ...(params.description === undefined ? undefined : { description: params.description }), + }).pipe( + Effect.mapError( + (error) => + new McpInvalidInputError({ + message: `Invalid escalation chain: ${String(error)}`, + parameter: "steps", + }), + ), + ) + const tenant = yield* CurrentMcpTenant + const chains = yield* EscalationChainsService + const chain = yield* chains + .create(tenant.orgId, tenant.userId, tenant.roles, request) + .pipe(Effect.catchTags(createChainErrors)) + const destinations = yield* destinationDirectory(tenant.orgId) + return { chain: toEscalationChainRow(chain, destinations) } + }), + render: (output) => ({ + title: "Escalation Chain Created", + blocks: chainBlocks(output.chain), + next: [ + doc.next( + "list_alert_rules", + {}, + `attach it with update_alert_rule escalation_chain_id="${output.chain.id}"`, + ), + ], + }), + }) +} diff --git a/apps/ai/src/mcp/tools/delete-escalation-chain.ts b/apps/ai/src/mcp/tools/delete-escalation-chain.ts new file mode 100644 index 0000000000..73dab98df8 --- /dev/null +++ b/apps/ai/src/mcp/tools/delete-escalation-chain.ts @@ -0,0 +1,52 @@ +import type { McpToolRegistrar } from "./types" +import { Effect, Schema } from "effect" +import { DeleteEscalationChainOutput } from "@maple/domain/mcp-outputs" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" +import { CurrentMcpTenant } from "../lib/query-warehouse" +import { deleteChainErrors, updateChainErrors } from "../lib/escalation-chains" +import * as P from "../lib/params" +import { doc } from "../lib/tool-doc" + +export function registerDeleteEscalationChainTool(server: McpToolRegistrar) { + server.define({ + name: "delete_escalation_chain", + title: "Delete Escalation Chain", + description: + 'Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id="none" first. ' + + "Escalations already running finish. Ids from list_escalation_chains.", + parameters: Schema.Struct({ + chain_id: P.resourceId("escalationChain", "Escalation chain to delete"), + }), + output: DeleteEscalationChainOutput, + hints: { readOnly: false, destructive: true, idempotent: false }, + phrases: ["Deleting an escalation chain"], + handler: Effect.fn("McpTool.deleteEscalationChain")(function* (params) { + const chainId = yield* P.decodeResourceId("escalationChain", "chain_id")(params.chain_id) + const tenant = yield* CurrentMcpTenant + const chains = yield* EscalationChainsService + // Read first so the reply names what was deleted. + const chain = yield* chains.get(tenant.orgId, chainId).pipe( + Effect.catchTags({ + "@maple/http/errors/EscalationChainNotFoundError": + updateChainErrors["@maple/http/errors/EscalationChainNotFoundError"], + "@maple/http/errors/AlertPersistenceError": + deleteChainErrors["@maple/http/errors/AlertPersistenceError"], + }), + ) + yield* chains + .delete(tenant.orgId, tenant.roles, chainId) + .pipe(Effect.catchTags(deleteChainErrors)) + return { id: chain.id, name: chain.name } + }), + render: (output) => ({ + title: "Escalation Chain Deleted", + blocks: [ + doc.fields([ + ["ID", output.id], + ["Name", output.name], + ]), + ], + next: [doc.next("list_escalation_chains", {}, "the remaining chains")], + }), + }) +} diff --git a/apps/ai/src/mcp/tools/get-alert-rule.ts b/apps/ai/src/mcp/tools/get-alert-rule.ts index f1d4b29b9d..ea74ed8d8a 100644 --- a/apps/ai/src/mcp/tools/get-alert-rule.ts +++ b/apps/ai/src/mcp/tools/get-alert-rule.ts @@ -1,13 +1,15 @@ import type { McpToolRegistrar } from "./types" import { toMcpHttpError } from "../lib/map-http-error" -import { Effect, Schema, Array as Arr } from "effect" +import { Effect, Option, Schema, Array as Arr } from "effect" import { GetAlertRuleOutput } from "@maple/domain/mcp-outputs" import { CurrentMcpTenant } from "../lib/query-warehouse" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" import { AlertsService } from "@maple/backend/services/alerts/AlertsService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import { formatCheckInterval, formatCondition, + formatEscalationChains, ruleConfigWarnings, ruleNotFound, slowedNotice, @@ -71,9 +73,30 @@ export function registerGetAlertRuleTool(server: McpToolRegistrar) { } }) + const row = toAlertRuleRow(rule) + // Chain names decorate the ids too; only a rule that uses a chain reads them. + const escalationChains = + row.escalationChains === undefined + ? undefined + : yield* (yield* EscalationChainsService).list(tenant.orgId).pipe( + Effect.map((r) => + Arr.map(row.escalationChains ?? [], (c) => ({ + ...c, + name: Option.getOrNull( + Option.map( + Arr.findFirst(r.chains, (chain) => chain.id === c.chainId), + (chain) => chain.name, + ), + ), + })), + ), + Effect.orElseSucceed(() => row.escalationChains), + ) + return { rule: { - ...toAlertRuleRow(rule), + ...row, + ...(escalationChains === undefined ? undefined : { escalationChains }), excludeServiceNames: [...rule.excludeServiceNames], groupBy: rule.groupBy ? [...rule.groupBy] : null, minimumSampleCount: rule.minimumSampleCount, @@ -107,6 +130,7 @@ export function registerGetAlertRuleTool(server: McpToolRegistrar) { ["Window", `${rule.windowMinutes}m, checked ${formatCheckInterval(rule)}`], ["Slowed down at", rule.slowedAt ?? undefined], ["Last evaluated", rule.lastEvaluatedAt ?? undefined], + ["Escalation", formatEscalationChains(rule.escalationChains)], ["Last evaluation error", rule.lastEvaluationError ?? undefined], [ "Last error kind", diff --git a/apps/ai/src/mcp/tools/list-escalation-chains.ts b/apps/ai/src/mcp/tools/list-escalation-chains.ts new file mode 100644 index 0000000000..11594e75b0 --- /dev/null +++ b/apps/ai/src/mcp/tools/list-escalation-chains.ts @@ -0,0 +1,64 @@ +import type { McpToolRegistrar } from "./types" +import { Array as Arr, Effect, Schema } from "effect" +import { ListEscalationChainsOutput } from "@maple/domain/mcp-outputs" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" +import { CurrentMcpTenant } from "../lib/query-warehouse" +import { toMcpHttpError } from "../lib/map-http-error" +import { + destinationDirectory, + formatRepeats, + formatSteps, + toEscalationChainRow, +} from "../lib/escalation-chains" +import { doc } from "../lib/tool-doc" + +export function registerListEscalationChainsTool(server: McpToolRegistrar) { + server.define({ + name: "list_escalation_chains", + title: "List Escalation Chains", + description: + "List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. " + + "Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.", + parameters: Schema.Struct({}), + output: ListEscalationChainsOutput, + hints: { readOnly: true }, + phrases: ["Listing escalation chains"], + handler: Effect.fn("McpTool.listEscalationChains")(function* () { + const tenant = yield* CurrentMcpTenant + const chains = yield* EscalationChainsService + const result = yield* chains + .list(tenant.orgId) + .pipe(Effect.mapError(toMcpHttpError("list_escalation_chains"))) + const destinations = result.chains.length === 0 ? [] : yield* destinationDirectory(tenant.orgId) + yield* Effect.annotateCurrentSpan({ + orgId: tenant.orgId, + "result.rowCount": result.chains.length, + }) + return { + chains: Arr.map(result.chains, (chain) => toEscalationChainRow(chain, destinations)), + total: result.chains.length, + } + }), + render: (output) => ({ + title: "Escalation Chains", + ...(output.chains.length === 0 + ? { + empty: { + message: + "No escalation chains yet. Create one with create_escalation_chain from destinations in list_alert_destinations.", + }, + } + : undefined), + blocks: Arr.flatMap(output.chains, (chain) => [ + doc.heading(`${chain.name} (${chain.id})`), + doc.text( + `${formatSteps(chain)}\nThe chain ${formatRepeats(chain.repeatCount)}. Used by ${chain.ruleIds.length} rule${chain.ruleIds.length === 1 ? "" : "s"}.`, + ), + ]), + next: + output.chains.length === 0 + ? [doc.next("list_alert_destinations", {}, "the destinations a step can notify")] + : [doc.next("list_alert_rules", {}, "attach a chain to a rule with update_alert_rule")], + }), + }) +} diff --git a/apps/ai/src/mcp/tools/mutating.ts b/apps/ai/src/mcp/tools/mutating.ts index f97be328e6..f21c8a9d46 100644 --- a/apps/ai/src/mcp/tools/mutating.ts +++ b/apps/ai/src/mcp/tools/mutating.ts @@ -19,6 +19,9 @@ export const MUTATING_TOOL_NAMES: ReadonlySet = new Set([ "update_alert_rule", "delete_alert_rule", "acknowledge_alert_incident", + "create_escalation_chain", + "update_escalation_chain", + "delete_escalation_chain", // error issues "claim_error_issue", "release_error_issue", diff --git a/apps/ai/src/mcp/tools/registry.ts b/apps/ai/src/mcp/tools/registry.ts index 2d974ac9f0..08718ab6db 100644 --- a/apps/ai/src/mcp/tools/registry.ts +++ b/apps/ai/src/mcp/tools/registry.ts @@ -11,6 +11,10 @@ import { registerPreviewAlertRuleTool } from "./preview-alert-rule" import { registerUpdateAlertRuleTool } from "./update-alert-rule" import { registerDeleteAlertRuleTool } from "./delete-alert-rule" import { registerAcknowledgeAlertIncidentTool } from "./acknowledge-alert-incident" +import { registerListEscalationChainsTool } from "./list-escalation-chains" +import { registerCreateEscalationChainTool } from "./create-escalation-chain" +import { registerUpdateEscalationChainTool } from "./update-escalation-chain" +import { registerDeleteEscalationChainTool } from "./delete-escalation-chain" import { registerCreateDashboardTool } from "./create-dashboard" import { registerDescribeDashboardSchemaTool } from "./describe-dashboard-schema" import { registerDiagnoseServiceTool } from "./diagnose-service" @@ -431,6 +435,10 @@ const collectMapleToolDefinitions = (): ReadonlyArray => { registerUpdateAlertRuleTool(registrar) registerDeleteAlertRuleTool(registrar) registerAcknowledgeAlertIncidentTool(registrar) + registerListEscalationChainsTool(registrar) + registerCreateEscalationChainTool(registrar) + registerUpdateEscalationChainTool(registrar) + registerDeleteEscalationChainTool(registrar) registerDescribeDashboardSchemaTool(registrar) registerListDashboardsTool(registrar) registerGetDashboardTool(registrar) diff --git a/apps/ai/src/mcp/tools/runtime-requirements.ts b/apps/ai/src/mcp/tools/runtime-requirements.ts index 340927704f..2bf4b85383 100644 --- a/apps/ai/src/mcp/tools/runtime-requirements.ts +++ b/apps/ai/src/mcp/tools/runtime-requirements.ts @@ -4,6 +4,7 @@ import type { AlertsService } from "@maple/backend/services/alerts/AlertsService import type { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" import type { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import type { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" +import type { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import type { DashboardPersistenceService } from "@maple/backend/services/dashboards/DashboardPersistenceService" import type { ErrorActorsService } from "@maple/backend/services/errors/ErrorActorsService" import type { ErrorIssueReadModelsService } from "@maple/backend/services/errors/ErrorIssueReadModelsService" @@ -48,6 +49,7 @@ export type McpToolRuntimeRequirements = | AlertReadModelsService | AlertIncidentAckService | AlertRulesService + | EscalationChainsService | DashboardPersistenceService | ErrorActorsService | ErrorIssueReadModelsService diff --git a/apps/ai/src/mcp/tools/update-alert-rule.ts b/apps/ai/src/mcp/tools/update-alert-rule.ts index 325300eebe..7101584acb 100644 --- a/apps/ai/src/mcp/tools/update-alert-rule.ts +++ b/apps/ai/src/mcp/tools/update-alert-rule.ts @@ -1,5 +1,5 @@ import { McpInvalidInputError, type McpToolRegistrar } from "./types" -import { Array as Arr, Effect, Schema } from "effect" +import { Array as Arr, Effect, Option, Schema, String as Str } from "effect" import { UpdateAlertRuleOutput } from "@maple/domain/mcp-outputs" import { toMcpHttpError } from "../lib/map-http-error" import { CurrentMcpTenant } from "../lib/query-warehouse" @@ -15,6 +15,7 @@ import { type AlertComparator, type AlertRuleDocument, type AlertThreshold, + type EscalationChainId, } from "@maple/domain/http" import { MAX_RAW_SQL_ALERT_GROUPS } from "@maple/domain/raw-sql" import { @@ -102,6 +103,10 @@ export const UpdateAlertRuleParameters = Schema.Struct({ notification_body: P.optionalText( "Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}", ), + escalation_chain_id: P.optionalResourceId( + "escalationChain", + 'Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or "none" to detach it. On a rule with warning and critical tiers it sets the critical tier', + ), }) /** @@ -129,7 +134,17 @@ export const buildUpdatedRequest = Effect.fnUntraced(function* ( const signalType = params.signal_type ?? current.signalType const comparator = params.comparator ?? current.comparator const keepsRawQuery = signalType === "raw_query" - const thresholds = yield* mergeThresholds(current, params, comparator) + const chain = + params.escalation_chain_id === undefined + ? undefined + : Str.toLowerCase(params.escalation_chain_id) === "none" + ? null + : yield* P.decodeResourceId( + "escalationChain", + "escalation_chain_id", + )(params.escalation_chain_id) + const merged = yield* mergeThresholds(current, params, comparator) + const thresholds = withEscalationChain(merged, chain) return { name: params.name ?? current.name, @@ -225,6 +240,17 @@ const mergeThresholds = ( ) } +/** Sets the chain on the only tier, or on the critical one of two; `undefined` keeps every tier's. */ +const withEscalationChain = ( + tiers: Arr.NonEmptyReadonlyArray, + chain: EscalationChainId | null | undefined, +): Arr.NonEmptyReadonlyArray => { + if (chain === undefined) return tiers + const critical = Arr.findLastIndex(tiers, (tier) => tier.severity === "critical") + const target = tiers.length === 1 ? 0 : Option.getOrElse(critical, () => tiers.length - 1) + return Arr.map(tiers, (tier, index) => (index === target ? { ...tier, escalationChainId: chain } : tier)) +} + /** `destination_ids` replaces; add/remove edit the current (or replaced) list in place. */ const mergeDestinations = ( current: ReadonlyArray, diff --git a/apps/ai/src/mcp/tools/update-escalation-chain.ts b/apps/ai/src/mcp/tools/update-escalation-chain.ts new file mode 100644 index 0000000000..df29eb0a9a --- /dev/null +++ b/apps/ai/src/mcp/tools/update-escalation-chain.ts @@ -0,0 +1,80 @@ +import { McpInvalidInputError, type McpToolRegistrar } from "./types" +import { Effect, Schema } from "effect" +import { EscalationChainUpdateRequest } from "@maple/domain/http" +import { EscalationChainMutationOutput } from "@maple/domain/mcp-outputs" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" +import { CurrentMcpTenant } from "../lib/query-warehouse" +import { + chainBlocks, + destinationDirectory, + StepsInput, + stepsParameterDescription, + toEscalationChainRow, + toStepsRequest, + updateChainErrors, +} from "../lib/escalation-chains" +import * as P from "../lib/params" +import { doc } from "../lib/tool-doc" + +const decodeUpdateRequest = Schema.decodeUnknownEffect(EscalationChainUpdateRequest) + +export function registerUpdateEscalationChainTool(server: McpToolRegistrar) { + server.define({ + name: "update_escalation_chain", + title: "Update Escalation Chain", + description: + "Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. " + + "Escalations already running finish on the steps they started with. Ids from list_escalation_chains.", + parameters: Schema.Struct({ + chain_id: P.resourceId("escalationChain", "Escalation chain to change"), + name: P.optionalText("New name"), + steps: P.optionalJson(StepsInput, `Replaces every step. ${stepsParameterDescription}`), + repeat_count: P.optionalNumber("Extra passes over the whole chain after the first, 0 to 5"), + description: P.optionalText("New notes"), + }), + output: EscalationChainMutationOutput, + hints: { readOnly: false, destructive: false, idempotent: true }, + phrases: ["Updating an escalation chain"], + handler: Effect.fn("McpTool.updateEscalationChain")(function* (params) { + const chainId = yield* P.decodeResourceId("escalationChain", "chain_id")(params.chain_id) + const steps = params.steps === undefined ? undefined : yield* toStepsRequest(params.steps) + if ( + params.name === undefined && + steps === undefined && + params.repeat_count === undefined && + params.description === undefined + ) { + return yield* new McpInvalidInputError({ + message: "Nothing to change: pass name, steps, repeat_count or description.", + parameter: "steps", + }) + } + const request = yield* decodeUpdateRequest({ + ...(params.name === undefined ? undefined : { name: params.name }), + ...(steps === undefined ? undefined : { steps }), + ...(params.repeat_count === undefined ? undefined : { repeatCount: params.repeat_count }), + ...(params.description === undefined ? undefined : { description: params.description }), + }).pipe( + Effect.mapError( + (error) => + new McpInvalidInputError({ + message: `Invalid escalation chain: ${String(error)}`, + parameter: "steps", + }), + ), + ) + const tenant = yield* CurrentMcpTenant + const chains = yield* EscalationChainsService + const chain = yield* chains + .update(tenant.orgId, tenant.userId, tenant.roles, chainId, request) + .pipe(Effect.catchTags(updateChainErrors)) + const destinations = yield* destinationDirectory(tenant.orgId) + return { chain: toEscalationChainRow(chain, destinations) } + }), + render: (output) => ({ + title: "Escalation Chain Updated", + blocks: chainBlocks(output.chain), + next: [doc.next("list_escalation_chains", {}, "every chain and the rules using it")], + }), + }) +} diff --git a/apps/ai/test/mcp/unimplemented-tool-services.ts b/apps/ai/test/mcp/unimplemented-tool-services.ts index ce8079677c..e8c89ce3ce 100644 --- a/apps/ai/test/mcp/unimplemented-tool-services.ts +++ b/apps/ai/test/mcp/unimplemented-tool-services.ts @@ -7,6 +7,7 @@ import { AlertsService } from "@maple/backend/services/alerts/AlertsService" import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import { DashboardPersistenceService } from "@maple/backend/services/dashboards/DashboardPersistenceService" import { ErrorActorsService } from "@maple/backend/services/errors/ErrorActorsService" import { ErrorIssueReadModelsService } from "@maple/backend/services/errors/ErrorIssueReadModelsService" @@ -44,6 +45,7 @@ export const unimplementedMcpToolServices = Layer.mergeAll( Layer.mock(AlertReadModelsService)({}), Layer.mock(AlertIncidentAckService)({}), Layer.mock(AlertRulesService)({}), + Layer.mock(EscalationChainsService)({}), Layer.mock(DashboardPersistenceService)({}), Layer.mock(ErrorActorsService)({}), Layer.mock(ErrorIssueReadModelsService)({}), diff --git a/apps/api/src/routes/v2/alerts.http.test.ts b/apps/api/src/routes/v2/alerts.http.test.ts index 9db81c6bf2..5c05555364 100644 --- a/apps/api/src/routes/v2/alerts.http.test.ts +++ b/apps/api/src/routes/v2/alerts.http.test.ts @@ -24,7 +24,7 @@ import { UserId, WarehouseQuotaExceededError, } from "@maple/domain/http" -import { MapleApiV2 } from "@maple/domain/http/v2" +import { encodePublicId, MapleApiV2 } from "@maple/domain/http/v2" import { BucketCacheService } from "@maple/query-engine/caching" import { EdgeCacheService } from "@maple/cache" import { CacheBackendLive } from "@maple/backend/platform/CacheBackendLive" @@ -645,6 +645,7 @@ describe("v2 alerts over HTTP", () => { upper: null, recovery_value: 0.015, destination_ids: [destination.body.id], + escalation_chain_id: null, }, { severity: "critical", @@ -652,6 +653,7 @@ describe("v2 alerts over HTTP", () => { upper: null, recovery_value: null, destination_ids: [pager.body.id], + escalation_chain_id: null, }, ] const created = await harness.request("POST", "/v2/alerts/rules", key.secret, { @@ -732,6 +734,7 @@ describe("v2 alerts over HTTP", () => { upper: null, recovery_value: null, destination_ids: [destination.body.id], + escalation_chain_id: null, }, ]) @@ -1289,4 +1292,173 @@ describe("v2 alerts over HTTP", () => { await harness.dispose() }) + + const createWebhook = (harness: ReturnType, token: string, name: string) => + harness.request("POST", "/v2/alerts/destinations", token, { + type: "webhook", + name, + url: `https://example.com/hooks/${name}`, + }) + + it("runs escalation chain CRUD and refuses to delete a chain a rule tier names", async () => { + const harness = makeHarness() + const key = await harness.bootstrapKey(["alerts:write"]) + const primary = await createWebhook(harness, key.secret, "primary") + const secondary = await createWebhook(harness, key.secret, "secondary") + const steps = [ + { targets: [{ type: "destination", destination_id: primary.body.id }], wait_minutes: 10 }, + { targets: [{ type: "destination", destination_id: secondary.body.id }], wait_minutes: 15 }, + ] + + const created = await harness.request("POST", "/v2/alerts/escalation_chains", key.secret, { + name: "Checkout on-call", + steps, + repeat_count: 1, + }) + expect(created.status, JSON.stringify(created.body)).toBe(200) + expect(created.body).toMatchObject({ + object: "escalation_chain", + name: "Checkout on-call", + description: null, + steps, + repeat_count: 1, + rule_ids: [], + }) + expect(created.body.id).toMatch(/^esc_/) + const chainId: string = created.body.id + + const listed = await harness.request("GET", "/v2/alerts/escalation_chains", key.secret) + expect(listed.status).toBe(200) + expect(listed.body).toMatchObject({ object: "list", has_more: false }) + expect(listed.body.data.map((chain: { id: string }) => chain.id)).toEqual([chainId]) + + const rule = await harness.request("POST", "/v2/alerts/rules", key.secret, { + name: "Checkout errors", + signal_type: "error_rate", + comparator: "gt", + window_minutes: 5, + thresholds: [ + { + severity: "critical", + value: 0.05, + destination_ids: [primary.body.id], + escalation_chain_id: chainId, + }, + ], + }) + expect(rule.status, JSON.stringify(rule.body)).toBe(200) + expect(rule.body.thresholds[0].escalation_chain_id).toBe(chainId) + + // A PATCH that leaves the tiers alone keeps the chain on them. + const toggled = await harness.request("PATCH", `/v2/alerts/rules/${rule.body.id}`, key.secret, { + enabled: false, + }) + expect(toggled.status).toBe(200) + expect(toggled.body.thresholds[0].escalation_chain_id).toBe(chainId) + + const fetched = await harness.request("GET", `/v2/alerts/escalation_chains/${chainId}`, key.secret) + expect(fetched.status).toBe(200) + expect(fetched.body.rule_ids).toEqual([rule.body.id]) + + const renamed = await harness.request( + "PATCH", + `/v2/alerts/escalation_chains/${chainId}`, + key.secret, + { + name: "Checkout paging", + repeat_count: 0, + steps: [steps[1]], + }, + ) + expect(renamed.status, JSON.stringify(renamed.body)).toBe(200) + expect(renamed.body).toMatchObject({ name: "Checkout paging", repeat_count: 0, steps: [steps[1]] }) + + const inUse = await harness.request("DELETE", `/v2/alerts/escalation_chains/${chainId}`, key.secret) + expect(inUse.status).toBe(409) + expect(inUse.body.error.code).toBe("escalation_chain_in_use") + + const detached = await harness.request("PATCH", `/v2/alerts/rules/${rule.body.id}`, key.secret, { + thresholds: [ + { + severity: "critical", + value: 0.05, + destination_ids: [primary.body.id], + escalation_chain_id: null, + }, + ], + }) + expect(detached.status).toBe(200) + expect(detached.body.thresholds[0].escalation_chain_id).toBeNull() + + const deleted = await harness.request("DELETE", `/v2/alerts/escalation_chains/${chainId}`, key.secret) + expect(deleted.status, JSON.stringify(deleted.body)).toBe(200) + expect(deleted.body).toEqual({ id: chainId, object: "escalation_chain", deleted: true }) + + const gone = await harness.request("GET", `/v2/alerts/escalation_chains/${chainId}`, key.secret) + expect(gone.status).toBe(404) + expect(gone.body.error.code).toBe("escalation_chain_not_found") + + await harness.dispose() + }) + + it("rejects escalation chains with unknown destinations or bad steps, and rules naming an unknown chain", async () => { + const harness = makeHarness() + const key = await harness.bootstrapKey(["alerts:write"]) + const primary = await createWebhook(harness, key.secret, "primary") + const post = (body: unknown) => + harness.request("POST", "/v2/alerts/escalation_chains", key.secret, body) + const step = (destinationId: string, waitMinutes = 10) => ({ + targets: [{ type: "destination", destination_id: destinationId }], + wait_minutes: waitMinutes, + }) + + const unknownDestination = await post({ + name: "Ghost", + steps: [step(encodePublicId("dest", "0b8f7c1e-2f61-4f1e-9d2a-3c4b5d6e7f80"))], + }) + const noSteps = await post({ name: "Empty", steps: [] }) + const noWait = await post({ name: "Instant", steps: [step(primary.body.id, 0)] }) + const tooManyRepeats = await post({ name: "Loop", steps: [step(primary.body.id)], repeat_count: 6 }) + for (const response of [unknownDestination, noSteps, noWait, tooManyRepeats]) { + expect(response.status, JSON.stringify(response.body)).toBe(400) + expect(response.body.error.type).toBe("invalid_request_error") + } + + const unknownChain = encodePublicId("esc", "0b8f7c1e-2f61-4f1e-9d2a-3c4b5d6e7f80") + const missing = await harness.request( + "PATCH", + `/v2/alerts/escalation_chains/${unknownChain}`, + key.secret, + { + name: "Nope", + }, + ) + expect(missing.status).toBe(404) + + const rule = await harness.request("POST", "/v2/alerts/rules", key.secret, { + name: "Unknown chain", + signal_type: "error_rate", + comparator: "gt", + window_minutes: 5, + thresholds: [ + { + severity: "critical", + value: 0.05, + destination_ids: [primary.body.id], + escalation_chain_id: unknownChain, + }, + ], + }) + expect(rule.status).toBe(400) + expect(rule.body.error.type).toBe("invalid_request_error") + + const readOnly = await harness.bootstrapKey(["alerts:read"]) + const denied = await harness.request("POST", "/v2/alerts/escalation_chains", readOnly.secret, { + name: "Denied", + steps: [step(primary.body.id)], + }) + expect(denied.status).toBe(403) + + await harness.dispose() + }) }) diff --git a/apps/api/src/routes/v2/escalation-chains.http.ts b/apps/api/src/routes/v2/escalation-chains.http.ts new file mode 100644 index 0000000000..824bdab589 --- /dev/null +++ b/apps/api/src/routes/v2/escalation-chains.http.ts @@ -0,0 +1,141 @@ +import { HttpApiBuilder } from "effect/http-api" +import { + CurrentTenant, + EscalationChainCreateRequest, + EscalationChainUpdateRequest, + type EscalationChainDocument, + type EscalationStep, +} from "@maple/domain/http" +import type { + V2EscalationChain, + V2EscalationChainDeleteResponse, + V2EscalationChainUpdateParams, + V2EscalationStep, +} from "@maple/domain/http/v2" +import { MapleApiV2, paginateArray, timestamp } from "@maple/domain/http/v2" +import { Array as Arr, Effect } from "effect" +import { auditDiff } from "@/routes/v2/audit-changes" +import { recordHttpAudit } from "@maple/backend/services/audit/AuditLogService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" + +const chainAuditDiff = auditDiff({ + fields: ["name", "description", "steps", "repeat_count"], +}) + +const toV2Step = (step: EscalationStep): V2EscalationStep => ({ + targets: Arr.map(step.targets, (target) => ({ type: target.type, destination_id: target.destinationId })), + wait_minutes: step.waitMinutes, +}) + +const fromV2Step = (step: V2EscalationStep): EscalationStep => ({ + targets: Arr.map(step.targets, (target) => ({ type: target.type, destinationId: target.destination_id })), + waitMinutes: step.wait_minutes, +}) + +export const toV2EscalationChain = (doc: EscalationChainDocument): V2EscalationChain => ({ + id: doc.id, + object: "escalation_chain", + name: doc.name, + description: doc.description, + steps: Arr.map(doc.steps, toV2Step), + repeat_count: doc.repeatCount, + rule_ids: doc.ruleIds, + created_at: timestamp(doc.createdAt), + updated_at: timestamp(doc.updatedAt), +}) + +export const HttpV2EscalationChainsLive = HttpApiBuilder.group(MapleApiV2, "escalationChains", (handlers) => + Effect.gen(function* () { + const chains = yield* EscalationChainsService + + return handlers + .handle("list", ({ query }) => + Effect.gen(function* () { + const tenant = yield* CurrentTenant.Context + // The service returns the org's whole (capped) set, so pages are cut from it. + const response = yield* chains.list(tenant.orgId) + const page = yield* paginateArray(Arr.map(response.chains, toV2EscalationChain), query) + return { object: "list" as const, ...page } + }), + ) + .handle("retrieve", ({ params }) => + Effect.gen(function* () { + const tenant = yield* CurrentTenant.Context + const chain = yield* chains.get(tenant.orgId, params.id) + return toV2EscalationChain(chain) + }), + ) + .handle("create", ({ payload }) => + Effect.gen(function* () { + const tenant = yield* CurrentTenant.Context + const created = yield* chains.create( + tenant.orgId, + tenant.userId, + tenant.roles, + new EscalationChainCreateRequest({ + name: payload.name, + ...(payload.description === undefined + ? undefined + : { description: payload.description }), + steps: Arr.map(payload.steps, fromV2Step), + ...(payload.repeat_count === undefined + ? undefined + : { repeatCount: payload.repeat_count }), + }), + ) + yield* recordHttpAudit("escalation_chain.created", { + resourceId: created.id, + metadata: { name: created.name, steps: created.steps.length }, + }) + return toV2EscalationChain(created) + }), + ) + .handle("update", ({ params, payload }) => + Effect.gen(function* () { + const tenant = yield* CurrentTenant.Context + const current = yield* chains.get(tenant.orgId, params.id) + const updated = yield* chains.update( + tenant.orgId, + tenant.userId, + tenant.roles, + params.id, + new EscalationChainUpdateRequest({ + ...(payload.name === undefined ? undefined : { name: payload.name }), + ...(payload.description === undefined + ? undefined + : { description: payload.description }), + ...(payload.steps === undefined + ? undefined + : { steps: Arr.map(payload.steps, fromV2Step) }), + ...(payload.repeat_count === undefined + ? undefined + : { repeatCount: payload.repeat_count }), + }), + ) + yield* recordHttpAudit("escalation_chain.updated", { + resourceId: updated.id, + changes: chainAuditDiff( + payload, + toV2EscalationChain(current), + toV2EscalationChain(updated), + ), + metadata: { name: updated.name }, + }) + return toV2EscalationChain(updated) + }), + ) + .handle("delete", ({ params }) => + Effect.gen(function* () { + const tenant = yield* CurrentTenant.Context + yield* chains.delete(tenant.orgId, tenant.roles, params.id) + yield* recordHttpAudit("escalation_chain.deleted", { resourceId: params.id }) + const deleted: V2EscalationChainDeleteResponse = { + id: params.id, + object: "escalation_chain", + deleted: true, + } + return deleted + }), + ) + }), +) diff --git a/apps/api/src/routes/v2/v2-test-support.ts b/apps/api/src/routes/v2/v2-test-support.ts index 33029d908c..3ab4aa35fa 100644 --- a/apps/api/src/routes/v2/v2-test-support.ts +++ b/apps/api/src/routes/v2/v2-test-support.ts @@ -7,6 +7,7 @@ import { AlertDestinationsService } from "@maple/backend/services/alerts/AlertDe import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import { AnomalyDetectionService } from "@maple/backend/services/alerts/AnomalyDetectionService" import { ErrorsService } from "@maple/backend/services/errors/ErrorsService" import { ErrorActorsService } from "@maple/backend/services/errors/ErrorActorsService" @@ -46,6 +47,7 @@ import { QueryEngineService } from "@maple/backend/services/warehouse/QueryEngin import { HttpV2AlertDeliveriesLive } from "./alert-deliveries.http" import { HttpV2AlertDestinationsLive } from "./alert-destinations.http" import { HttpV2AlertIncidentsLive } from "./alert-incidents.http" +import { HttpV2EscalationChainsLive } from "./escalation-chains.http" import { HttpV2AlertRulesLive } from "./alert-rules.http" import { HttpV2ApiKeysLive } from "./api-keys.http" import { HttpV2AttributeMappingsLive } from "./attribute-mappings.http" @@ -187,6 +189,8 @@ const v2GroupLayersExceptOnboardingChecklist = ( HttpV2AlertIncidentsLive.pipe( Layer.provide(Layer.merge(AlertIncidentAckService.layer, OrgMembersServiceStubLayer)), ), + // Real service: it needs only the Database every harness already provides. + HttpV2EscalationChainsLive.pipe(Layer.provide(EscalationChainsService.layer)), HttpV2IngestKeysLive, HttpV2SourcesLive, HttpV2ErrorIssuesLive, diff --git a/apps/api/src/runtime/http-graph.ts b/apps/api/src/runtime/http-graph.ts index 7502760091..18e96ea3c2 100644 --- a/apps/api/src/runtime/http-graph.ts +++ b/apps/api/src/runtime/http-graph.ts @@ -45,6 +45,7 @@ import { ClerkWebhookRouter } from "@/routes/webhooks/clerk.http" import { HttpV2AlertDeliveriesLive } from "@/routes/v2/alert-deliveries.http" import { HttpV2AlertDestinationsLive } from "@/routes/v2/alert-destinations.http" import { HttpV2AlertIncidentsLive } from "@/routes/v2/alert-incidents.http" +import { HttpV2EscalationChainsLive } from "@/routes/v2/escalation-chains.http" import { HttpV2AlertRulesLive } from "@/routes/v2/alert-rules.http" import { HttpV2AnomaliesLive } from "@/routes/v2/anomalies.http" import { HttpV2ApiKeysLive } from "@/routes/v2/api-keys.http" @@ -182,6 +183,7 @@ const ApiV2Routes = HttpApiBuilder.layer(MapleApiV2).pipe( HttpV2AlertRulesLive, HttpV2AlertDestinationsLive, HttpV2AlertIncidentsLive, + HttpV2EscalationChainsLive, HttpV2IngestKeysLive, HttpV2SourcesLive, HttpV2ChatIntegrationsLive, diff --git a/apps/api/src/runtime/service-graph.ts b/apps/api/src/runtime/service-graph.ts index 774172532c..2a9ca26711 100644 --- a/apps/api/src/runtime/service-graph.ts +++ b/apps/api/src/runtime/service-graph.ts @@ -11,6 +11,7 @@ import { AlertDestinationsService } from "@maple/backend/services/alerts/AlertDe import { AlertReadModelsService } from "@maple/backend/services/alerts/AlertReadModelsService" import { AlertIncidentAckService } from "@maple/backend/services/alerts/AlertIncidentAckService" import { AlertRulesService } from "@maple/backend/services/alerts/AlertRulesService" +import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import { AnomalyDetectionService } from "@maple/backend/services/alerts/AnomalyDetectionService" import { PlanetScaleOAuthService } from "@maple/backend/services/auth/PlanetScaleOAuthService" @@ -179,6 +180,7 @@ export const HttpServicesLive = Layer.mergeAll( AlertIncidentAckService.layer, AlertRulesService.layer, AlertsService.layer, + EscalationChainsService.layer, AnomalyDetectionService.layer, AiTriageService.layer, InvestigationService.layer, diff --git a/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json b/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json index 6b53854b63..95e04f3929 100644 --- a/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json +++ b/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json @@ -638,6 +638,7 @@ "destination_ids": [ "dest_oybbpTBhtSFGShMjjLiCrh" ], + "escalation_chain_id": null, "recovery_value": null, "severity": "critical", "upper": null, @@ -1293,6 +1294,7 @@ "destination_ids": [ "dest_oybbpTBhtSFGShMjjLiCrh" ], + "escalation_chain_id": null, "recovery_value": null, "severity": "critical", "upper": null, @@ -1417,6 +1419,10 @@ }, "type": "array" }, + "escalation_chain_id": { + "$ref": "#/components/schemas/_maple_EscalationChainId", + "description": "Escalation chain (`esc_…`) an incident at this severity starts, on top of `destination_ids`, or `null` (the default) for none. It pages step by step until the incident is acknowledged or resolved." + }, "recovery_value": { "description": "The value the signal must cross back over before this tier clears, or `null` (the default) to clear as soon as it stops breaching. Must sit on the healthy side of `value`.", "examples": [ @@ -7166,6 +7172,15 @@ "title": "Public ID", "type": "string" }, + "_maple_EscalationChainId": { + "description": "Opaque, prefixed public object ID (e.g. `esc_YofPTrK9782DWwcnXhpcCw`). A reversible base58 encoding of the internal ID — treat it as an opaque string.", + "examples": [ + "esc_YofPTrK9782DWwcnXhpcCw" + ], + "format": "maple.public_id", + "title": "Public ID", + "type": "string" + }, "_maple_IssueKind": { "enum": [ "error", @@ -14053,6 +14068,10 @@ "description": "The incident history produced by your alert rules. Incidents open when a rule breaches for enough consecutive checks and resolve automatically once the signal recovers. An open incident can be acknowledged to pause its reminders.", "name": "Alert Incidents" }, + { + "description": "Step-by-step paging for alert incidents: notify the first responder, wait for an acknowledgement, then widen the circle. A rule's severity tier names the chain it starts.", + "name": "Escalation Chains" + }, { "description": "Telemetry ingest credentials for your organization: a public key for client-side senders and a private key for server-side senders. Retrieve them or roll either key. All operations are admin-only.", "name": "Ingest Keys" diff --git a/apps/landing/src/content/docs/reference/mcp.md b/apps/landing/src/content/docs/reference/mcp.md index 82a9aba088..34a6d3f378 100644 --- a/apps/landing/src/content/docs/reference/mcp.md +++ b/apps/landing/src/content/docs/reference/mcp.md @@ -114,6 +114,10 @@ Tools that take an alert rule, alert destination, dashboard or error issue ID ac | `create_alert_rule` | write | Create an alert rule from a template or from signal type, comparator and threshold | | `update_alert_rule` | write | Change the fields you pass on an alert rule | | `delete_alert_rule` | write | Delete an alert rule and its incident history. Requires `confirm=true` | +| `list_escalation_chains` | read | Escalation chains with their steps, repeats and the rules using them | +| `create_escalation_chain` | write | Page destinations step by step, waiting for an acknowledgement between steps | +| `update_escalation_chain` | write | Change a chain's name, steps, repeats or description | +| `delete_escalation_chain` | write | Delete a chain no rule uses | ### Dashboards diff --git a/apps/web/e2e/tests/alert-rules.e2e.ts b/apps/web/e2e/tests/alert-rules.e2e.ts index df8807f0ef..feeef87386 100644 --- a/apps/web/e2e/tests/alert-rules.e2e.ts +++ b/apps/web/e2e/tests/alert-rules.e2e.ts @@ -11,6 +11,7 @@ interface Tier { upper: number | null recovery_value: number | null destination_ids: string[] + escalation_chain_id: string | null } interface Rule { id: string @@ -51,6 +52,7 @@ test.describe("alert rules", () => { upper: null, recovery_value: 0.005, destination_ids: [warn.body.id], + escalation_chain_id: null, }, { severity: "critical", @@ -58,6 +60,7 @@ test.describe("alert rules", () => { upper: null, recovery_value: null, destination_ids: [page.body.id], + escalation_chain_id: null, }, ] const created = await api(browser, appUrl, "/v2/alerts/rules", { diff --git a/apps/web/src/page/alerts/editor/form-steps.ts b/apps/web/src/page/alerts/editor/form-steps.ts index b15b2f6a87..f3c2ea3263 100644 --- a/apps/web/src/page/alerts/editor/form-steps.ts +++ b/apps/web/src/page/alerts/editor/form-steps.ts @@ -36,7 +36,12 @@ export const setSeverity = (form: RuleForm, severity: AlertSeverity): RuleForm = export const addCriticalTier = (form: RuleForm): RuleForm => ({ ...form, severity: "warning", - escalation: { threshold: suggestCriticalThreshold(form), recoveryValue: "", destinationIds: [] }, + escalation: { + threshold: suggestCriticalThreshold(form), + recoveryValue: "", + destinationIds: [], + escalationChainId: null, + }, }) /** Services: each named service is already its own group, and exclude only narrows "all services". */ diff --git a/apps/web/src/page/alerts/editor/form.ts b/apps/web/src/page/alerts/editor/form.ts index 381801e6db..489145a7d2 100644 --- a/apps/web/src/page/alerts/editor/form.ts +++ b/apps/web/src/page/alerts/editor/form.ts @@ -1,4 +1,10 @@ -import { AlertComparator, AlertDestinationId, AlertSeverity, AlertSignalType } from "@maple/domain/http" +import { + AlertComparator, + AlertDestinationId, + AlertSeverity, + AlertSignalType, + EscalationChainId, +} from "@maple/domain/http" import { QueryEngineAlertReducer } from "@maple/domain/query-engine" import type { RuleFormState } from "@maple/web-data/alerts/form-utils" import { Schema } from "effect" @@ -14,6 +20,7 @@ export const EscalationTier = Schema.Struct({ threshold: Schema.String, recoveryValue: Schema.String, destinationIds: DestinationIds, + escalationChainId: Schema.NullOr(EscalationChainId), }) export const RuleForm = Schema.Struct({ @@ -45,6 +52,7 @@ export const RuleForm = Schema.Struct({ rawQuerySql: Schema.String, rawQueryReducer: QueryEngineAlertReducer, destinationIds: DestinationIds, + escalationChainId: Schema.NullOr(EscalationChainId), notificationTitle: Schema.String, notificationBody: Schema.String, }) diff --git a/apps/web/src/page/investigations/conversation/kit/tool-metadata.ts b/apps/web/src/page/investigations/conversation/kit/tool-metadata.ts index a9f025bd53..5b893504fd 100644 --- a/apps/web/src/page/investigations/conversation/kit/tool-metadata.ts +++ b/apps/web/src/page/investigations/conversation/kit/tool-metadata.ts @@ -64,6 +64,10 @@ const TOOL_LABELS = new Map([ ["list_alert_checks", "List Alert Checks"], ["preview_alert_rule", "Preview Alert Rule"], ["get_incident_timeline", "Incident Timeline"], + ["list_escalation_chains", "List Escalation Chains"], + ["create_escalation_chain", "Create Escalation Chain"], + ["update_escalation_chain", "Update Escalation Chain"], + ["delete_escalation_chain", "Delete Escalation Chain"], ["get_error_notification_policy", "Notification Policy"], ["update_error_notification_policy", "Notification Policy"], // sessions @@ -133,6 +137,10 @@ const TOOL_ICONS = new Map([ ["list_alert_checks", "CircleCheckIcon"], ["preview_alert_rule", "HistoryIcon"], ["get_incident_timeline", "HistoryIcon"], + ["list_escalation_chains", "BellIcon"], + ["create_escalation_chain", "BellIcon"], + ["update_escalation_chain", "BellIcon"], + ["delete_escalation_chain", "TrashIcon"], ["get_error_notification_policy", "BellIcon"], ["update_error_notification_policy", "BellIcon"], ["run_sql", "DatabaseIcon"], diff --git a/packages/domain/src/audit-actions.ts b/packages/domain/src/audit-actions.ts index 0c9b5fd48c..84c1987354 100644 --- a/packages/domain/src/audit-actions.ts +++ b/packages/domain/src/audit-actions.ts @@ -67,6 +67,10 @@ export const AuditResources = { device_credential: { prefix: PublicIdPrefixes.deviceCredential, verbs: ["registered", "revoked"] }, /** Verbs mirror the issue event types: `recordEvent` audits every one it attributes. */ error_issue: { prefix: PublicIdPrefixes.errorIssue, verbs: ErrorIssueEventType.literals }, + escalation_chain: { + prefix: PublicIdPrefixes.escalationChain, + verbs: ["created", "updated", "deleted"], + }, /** One connected Google Cloud scope; the scope and what it collects are in `metadata`. */ gcp_connector: { prefix: PublicIdPrefixes.gcpConnector, @@ -187,6 +191,7 @@ export const AuditResourceLabels = { ai_provider_key: "AI provider key", alert_destination: "Alert destination", alert_rule: "Alert rule", + escalation_chain: "Escalation chain", anomaly_incident: "Anomaly", anomaly_settings: "Anomaly detection", api: "API", @@ -237,6 +242,9 @@ export const AuditActionLabels = { "alert_rule.created": "Created alert rule", "alert_rule.updated": "Updated alert rule", "alert_rule.deleted": "Deleted alert rule", + "escalation_chain.created": "Created escalation chain", + "escalation_chain.updated": "Updated escalation chain", + "escalation_chain.deleted": "Deleted escalation chain", "anomaly_incident.resolved": "Resolved anomaly", "anomaly_settings.updated": "Updated anomaly detection", "api.request": "Made API request", diff --git a/packages/domain/src/generated/anticipated-error-identifiers.ts b/packages/domain/src/generated/anticipated-error-identifiers.ts index 441ce1dac5..d85a83750b 100644 --- a/packages/domain/src/generated/anticipated-error-identifiers.ts +++ b/packages/domain/src/generated/anticipated-error-identifiers.ts @@ -65,6 +65,8 @@ export const ANTICIPATED_ERROR_IDENTIFIER_LIST: ReadonlyArray = [ "@maple/http/errors/ErrorIssueTransitionError", "@maple/http/errors/ErrorNotificationUnknownDestinationError", "@maple/http/errors/ErrorValidationError", + "@maple/http/errors/EscalationChainInUseError", + "@maple/http/errors/EscalationChainNotFoundError", "@maple/http/errors/GcpMetricsUnavailableError", "@maple/http/errors/GcpScopeAlreadyConnectedError", "@maple/http/errors/IncidentTriageUnauthorizedError", diff --git a/packages/domain/src/http/v2/alert-rules.ts b/packages/domain/src/http/v2/alert-rules.ts index a475185bb8..8b0650879f 100644 --- a/packages/domain/src/http/v2/alert-rules.ts +++ b/packages/domain/src/http/v2/alert-rules.ts @@ -38,7 +38,7 @@ import { wireExample, ListOf, ListQuery, Timestamp } from "./envelopes" import { V2ParameterInvalid } from "./errors" import { publicError, publicErrors } from "./public-error" import { V2ManagedWarehouseErrors, V2QueryEngineRouteErrors, V2WarehouseErrors } from "./query-errors" -import { AlertIncidentPublicId, AlertRulePublicId } from "./resource-ids" +import { AlertIncidentPublicId, AlertRulePublicId, EscalationChainPublicId } from "./resource-ids" export { AlertIncidentPublicId, AlertRulePublicId } from "./resource-ids" @@ -73,6 +73,12 @@ export const V2AlertThreshold = Schema.Struct({ description: "Destinations (`dest_…`) this severity notifies. A `critical` incident also notifies every `warning` destination.", }), + escalation_chain_id: Schema.NullOr(EscalationChainPublicId) + .annotate({ + description: + "Escalation chain (`esc_…`) an incident at this severity starts, on top of `destination_ids`, or `null` (the default) for none. It pages step by step until the incident is acknowledged or resolved.", + }) + .pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))), }).annotate({ identifier: "AlertThreshold", title: "Alert threshold", @@ -93,6 +99,7 @@ export const fromV2Threshold = (tier: V2AlertThreshold): AlertThreshold => ({ upper: tier.upper, recoveryValue: tier.recovery_value, destinationIds: tier.destination_ids, + escalationChainId: tier.escalation_chain_id, }) export const toV2Threshold = (tier: AlertThreshold): V2AlertThreshold => ({ @@ -101,6 +108,7 @@ export const toV2Threshold = (tier: AlertThreshold): V2AlertThreshold => ({ upper: tier.upper, recovery_value: tier.recoveryValue, destination_ids: tier.destinationIds, + escalation_chain_id: tier.escalationChainId ?? null, }) /** Mirrors the v1 caps (alerts.ts): 32-char tags, at most 20 per rule. */ @@ -143,6 +151,7 @@ const alertRuleExample = { upper: null, recovery_value: null, destination_ids: ["dest_oybbpTBhtSFGShMjjLiCrh"], + escalation_chain_id: null, }, ], window_minutes: 5, diff --git a/packages/domain/src/http/v2/api.ts b/packages/domain/src/http/v2/api.ts index 6ab2a987bb..8937f90b27 100644 --- a/packages/domain/src/http/v2/api.ts +++ b/packages/domain/src/http/v2/api.ts @@ -3,6 +3,7 @@ import { V2AlertDeliveriesApiGroup } from "./alert-deliveries" import { V2AlertDestinationsApiGroup } from "./alert-destinations" import { V2AnomaliesApiGroup } from "./anomalies" import { V2AlertIncidentsApiGroup } from "./alert-incidents" +import { V2EscalationChainsApiGroup } from "./escalation-chains" import { V2AlertRulesApiGroup } from "./alert-rules" import { V2ApiKeysApiGroup } from "./api-keys" import { V2AttributeMappingsApiGroup } from "./attribute-mappings" @@ -104,6 +105,7 @@ export class MapleApiV2 extends HttpApi.make("MapleApiV2") .add(V2AlertDeliveriesApiGroup) .add(V2AlertDestinationsApiGroup) .add(V2AlertIncidentsApiGroup) + .add(V2EscalationChainsApiGroup) .add(V2IngestKeysApiGroup) .add(V2SourcesApiGroup) .add(V2ChatIntegrationsApiGroup) diff --git a/packages/domain/src/http/v2/escalation-chains.ts b/packages/domain/src/http/v2/escalation-chains.ts new file mode 100644 index 0000000000..786633b7d9 --- /dev/null +++ b/packages/domain/src/http/v2/escalation-chains.ts @@ -0,0 +1,264 @@ +import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/http-api" +import { Array as Arr, Schema } from "effect" +import { AlertForbiddenError, AlertPersistenceError, AlertValidationError } from "../alerts" +import { + EscalationChainDescription, + EscalationChainInUseError, + EscalationChainName, + EscalationChainNotFoundError, + EscalationRepeatCount, + EscalationWaitMinutes, + MAX_ESCALATION_REPEATS, + MAX_ESCALATION_STEPS, + MAX_ESCALATION_STEP_TARGETS, + MAX_ESCALATION_WAIT_MINUTES, +} from "../escalation-chains" +import { AuthorizationV2 } from "./auth" +import { wireExample, ListOf, ListQuery, Timestamp } from "./envelopes" +import { V2ParameterInvalid } from "./errors" +import { publicErrors } from "./public-error" +import { AlertDestinationPublicId, AlertRulePublicId, EscalationChainPublicId } from "./resource-ids" + +export { EscalationChainPublicId } from "./resource-ids" + +export const V2EscalationTarget = Schema.Struct({ + type: Schema.Literal("destination").annotate({ + description: 'What the step notifies. Always `"destination"` for now.', + }), + destination_id: AlertDestinationPublicId.annotate({ + description: "The alert destination (`dest_…`) this step notifies.", + }), +}).annotate({ + identifier: "EscalationTarget", + title: "Escalation target", + description: "Who one escalation step notifies.", +}) +export type V2EscalationTarget = Schema.Schema.Type + +// Bounded arrays on the wire, not tuples: generated clients do not all read `prefixItems`. +// The refine narrows the decoded type to non-empty and goes first, so the checks carry the docs. +const V2EscalationTargets = Schema.Array(V2EscalationTarget) + .pipe(Schema.refine(Arr.isReadonlyArrayNonEmpty)) + .check(Schema.isMinLength(1), Schema.isMaxLength(MAX_ESCALATION_STEP_TARGETS)) + +export const V2EscalationStep = Schema.Struct({ + targets: V2EscalationTargets.annotate({ + description: `Who this step notifies, all at once: 1 to ${MAX_ESCALATION_STEP_TARGETS} targets.`, + }), + wait_minutes: EscalationWaitMinutes.annotate({ + description: `Minutes to wait for an acknowledgement before the next step (1 to ${MAX_ESCALATION_WAIT_MINUTES}).`, + examples: [10], + }), +}).annotate({ + identifier: "EscalationStep", + title: "Escalation step", + description: "Notify every target, then wait `wait_minutes` for an acknowledgement before the next step.", +}) +export type V2EscalationStep = Schema.Schema.Type + +const V2EscalationSteps = Schema.Array(V2EscalationStep) + .pipe(Schema.refine(Arr.isReadonlyArrayNonEmpty)) + .check(Schema.isMinLength(1), Schema.isMaxLength(MAX_ESCALATION_STEPS)) + .annotate({ description: `The steps in order, 1 to ${MAX_ESCALATION_STEPS}.` }) + +const RepeatCount = EscalationRepeatCount.annotate({ + description: `Extra passes over the whole chain after the first, 0 to ${MAX_ESCALATION_REPEATS}. \`0\` runs it once.`, + examples: [1], +}) + +const escalationChainExample = { + id: "esc_gQtGH2s65G6a7sJmuayXL6", + object: "escalation_chain", + name: "Checkout on-call", + description: "Page the primary, then the secondary, then the team lead.", + steps: [ + { + targets: [{ type: "destination", destination_id: "dest_aMadCYFsFjn8nerC3aEWyG" }], + wait_minutes: 10, + }, + { + targets: [{ type: "destination", destination_id: "dest_cTp8s4DaBKsNmcma6yJGSn" }], + wait_minutes: 15, + }, + ], + repeat_count: 1, + rule_ids: ["alrt_pJsL1o13V4GRjetnBTaWLv"], + created_at: "2026-07-15T09:00:00.000Z", + updated_at: "2026-07-15T09:00:00.000Z", +} as const + +export const V2EscalationChain = Schema.Struct({ + id: EscalationChainPublicId, + object: Schema.Literal("escalation_chain").annotate({ + description: 'The object type, always `"escalation_chain"`.', + examples: ["escalation_chain"], + }), + name: Schema.String.annotate({ description: "Display name.", examples: ["Checkout on-call"] }), + description: Schema.NullOr(Schema.String).annotate({ description: "Free-form notes, or `null`." }), + steps: V2EscalationSteps, + repeat_count: RepeatCount, + rule_ids: Schema.Array(AlertRulePublicId).annotate({ + description: + "Alert rules (`alrt_…`) with a severity tier that starts this chain. A chain in use cannot be deleted.", + }), + created_at: Timestamp, + updated_at: Timestamp, +}).annotate({ + identifier: "EscalationChain", + title: "Escalation Chain", + description: + "An ordered paging policy. When an incident opens at a severity tier that names the chain, the tier's own destinations fire at once and the chain runs on top: each step notifies its targets, then waits for an acknowledgement before the next. Escalation stops when the incident is acknowledged or resolved.", + examples: [wireExample(escalationChainExample)], +}) +export type V2EscalationChain = Schema.Schema.Type + +export const V2EscalationChainCreateParams = Schema.Struct({ + name: EscalationChainName.annotate({ description: "Display name, 1 to 120 characters." }), + description: Schema.optionalKey( + Schema.NullOr(EscalationChainDescription).annotate({ + description: "Free-form notes, at most 500 characters.", + }), + ), + steps: V2EscalationSteps, + repeat_count: Schema.optionalKey(RepeatCount), +}).annotate({ + identifier: "EscalationChainCreateParams", + title: "Create escalation chain", + description: "Every destination a step names must exist in your organization.", + examples: [ + wireExample({ + name: escalationChainExample.name, + steps: escalationChainExample.steps, + repeat_count: 1, + }), + ], +}) +export type V2EscalationChainCreateParams = Schema.Schema.Type + +export const V2EscalationChainUpdateParams = Schema.Struct({ + name: Schema.optionalKey(EscalationChainName), + description: Schema.optionalKey(Schema.NullOr(EscalationChainDescription)), + steps: Schema.optionalKey( + V2EscalationSteps.annotate({ + description: "Replaces every step. Running escalations keep their own copy.", + }), + ), + repeat_count: Schema.optionalKey(RepeatCount), +}).annotate({ + identifier: "EscalationChainUpdateParams", + title: "Update escalation chain", + description: + "Omitted fields are unchanged. An escalation already running finishes on the chain it started with.", + examples: [wireExample({ repeat_count: 2 })], +}) +export type V2EscalationChainUpdateParams = Schema.Schema.Type + +export const V2EscalationChainDeleteResponse = Schema.Struct({ + id: EscalationChainPublicId, + object: Schema.Literal("escalation_chain").annotate({ + description: 'The object type, always `"escalation_chain"`.', + }), + deleted: Schema.Literal(true).annotate({ description: "Always `true`: the chain no longer exists." }), +}).annotate({ + identifier: "EscalationChainDeleteResponse", + title: "Escalation chain delete response", + description: "Confirmation that an escalation chain was deleted.", + examples: [wireExample({ id: escalationChainExample.id, object: "escalation_chain", deleted: true })], +}) +export type V2EscalationChainDeleteResponse = Schema.Schema.Type + +const EscalationChainList = ListOf(V2EscalationChain).annotate({ + identifier: "EscalationChainList", + title: "Escalation chain list", + description: "A cursor-paginated page of escalation chains, by name.", +}) + +const [alertForbidden, alertValidation, alertPersistence, chainNotFound, chainInUse] = publicErrors( + AlertForbiddenError, + AlertValidationError, + AlertPersistenceError, + EscalationChainNotFoundError, + EscalationChainInUseError, +) + +export class V2EscalationChainsApiGroup extends HttpApiGroup.make("escalationChains") + .add( + HttpApiEndpoint.get("list", "/", { + query: ListQuery, + success: EscalationChainList, + error: [V2ParameterInvalid.schema, alertPersistence], + }).annotateMerge( + OpenApi.annotations({ + identifier: "listEscalationChains", + summary: "List escalation chains", + description: + "Returns your organization's escalation chains by name, each with the rules that use it. Requires the `alerts:read` scope.", + }), + ), + ) + .add( + HttpApiEndpoint.get("retrieve", "/:id", { + params: { id: EscalationChainPublicId }, + success: V2EscalationChain, + error: [chainNotFound, alertPersistence], + }).annotateMerge( + OpenApi.annotations({ + identifier: "getEscalationChain", + summary: "Retrieve an escalation chain", + description: + "Returns a single escalation chain by its `esc_…` ID. Requires the `alerts:read` scope.", + }), + ), + ) + .add( + HttpApiEndpoint.post("create", "/", { + payload: V2EscalationChainCreateParams, + success: V2EscalationChain, + error: [alertForbidden, alertValidation, alertPersistence], + }).annotateMerge( + OpenApi.annotations({ + identifier: "createEscalationChain", + summary: "Create an escalation chain", + description: + "Creates a chain. Attach it to an alert rule through a severity tier's `escalation_chain_id`. Fails with an `invalid_request_error` when a step names an unknown destination. Requires a role that can manage alerts and the `alerts:write` scope.", + }), + ), + ) + .add( + HttpApiEndpoint.patch("update", "/:id", { + params: { id: EscalationChainPublicId }, + payload: V2EscalationChainUpdateParams, + success: V2EscalationChain, + error: [alertForbidden, alertValidation, alertPersistence, chainNotFound], + }).annotateMerge( + OpenApi.annotations({ + identifier: "updateEscalationChain", + summary: "Update an escalation chain", + description: + "Changes the name, description, steps or repeat count. New incidents use the new steps. Requires the `alerts:write` scope.", + }), + ), + ) + .add( + HttpApiEndpoint.delete("delete", "/:id", { + params: { id: EscalationChainPublicId }, + success: V2EscalationChainDeleteResponse, + error: [alertForbidden, alertPersistence, chainNotFound, chainInUse], + }).annotateMerge( + OpenApi.annotations({ + identifier: "deleteEscalationChain", + summary: "Delete an escalation chain", + description: + "Deletes a chain. Fails with a `conflict_error` while any alert rule tier still names it; detach it from those rules first. Requires the `alerts:write` scope.", + }), + ), + ) + .prefix("/v2/alerts/escalation_chains") + .middleware(AuthorizationV2) + .annotateMerge( + OpenApi.annotations({ + title: "Escalation Chains", + description: + "Step-by-step paging for alert incidents: notify the first responder, wait for an acknowledgement, then widen the circle. A rule's severity tier names the chain it starts.", + }), + ) {} diff --git a/packages/domain/src/http/v2/index.ts b/packages/domain/src/http/v2/index.ts index 574394f678..6ff4aff3f9 100644 --- a/packages/domain/src/http/v2/index.ts +++ b/packages/domain/src/http/v2/index.ts @@ -13,6 +13,7 @@ export * from "./dashboards" export * from "./envelopes" export * from "./errors" export * from "./error-issues" +export * from "./escalation-chains" export * from "./ingest-keys" export * from "./sources" export * from "./integrations-chat" diff --git a/packages/domain/src/http/v2/openapi.test.ts b/packages/domain/src/http/v2/openapi.test.ts index ff2669f0ae..87da9e8601 100644 --- a/packages/domain/src/http/v2/openapi.test.ts +++ b/packages/domain/src/http/v2/openapi.test.ts @@ -81,6 +81,7 @@ describe("MapleApiV2 OpenAPI", () => { expect(surface).toEqual([ "DELETE /v2/alerts/destinations/{id}", + "DELETE /v2/alerts/escalation_chains/{id}", "DELETE /v2/alerts/rules/{id}", "DELETE /v2/api_keys/{id}", "DELETE /v2/attribute_mappings/{id}", @@ -109,6 +110,8 @@ describe("MapleApiV2 OpenAPI", () => { "GET /v2/alerts/deliveries", "GET /v2/alerts/destinations", "GET /v2/alerts/destinations/{id}", + "GET /v2/alerts/escalation_chains", + "GET /v2/alerts/escalation_chains/{id}", "GET /v2/alerts/incidents", "GET /v2/alerts/incidents/{id}", "GET /v2/alerts/incidents/{id}/events", @@ -196,6 +199,7 @@ describe("MapleApiV2 OpenAPI", () => { "GET /v2/users/{user_id}/issues", "GET /v2/widget_summary", "PATCH /v2/alerts/destinations/{id}", + "PATCH /v2/alerts/escalation_chains/{id}", "PATCH /v2/alerts/rules/{id}", "PATCH /v2/anomalies/settings", "PATCH /v2/attribute_mappings/{id}", @@ -214,6 +218,7 @@ describe("MapleApiV2 OpenAPI", () => { "POST /v2/alerts/destinations", "POST /v2/alerts/destinations/telegram/chats", "POST /v2/alerts/destinations/{id}/test", + "POST /v2/alerts/escalation_chains", "POST /v2/alerts/incidents/{id}/acknowledge", "POST /v2/alerts/incidents/{id}/unacknowledge", "POST /v2/alerts/rules", diff --git a/packages/domain/src/http/v2/public-id.ts b/packages/domain/src/http/v2/public-id.ts index 086531fd75..e747c160cf 100644 --- a/packages/domain/src/http/v2/public-id.ts +++ b/packages/domain/src/http/v2/public-id.ts @@ -36,6 +36,7 @@ export const PublicIdPrefixes = { alertRule: "alrt", alertDestination: "dest", alertIncident: "inc", + escalationChain: "esc", actor: "actor", auditLogEntry: "alog", errorIssue: "iss", diff --git a/packages/domain/src/http/v2/resource-ids.ts b/packages/domain/src/http/v2/resource-ids.ts index ef5aa99576..df01f47218 100644 --- a/packages/domain/src/http/v2/resource-ids.ts +++ b/packages/domain/src/http/v2/resource-ids.ts @@ -10,6 +10,7 @@ import { DeviceCredentialId, ErrorIncidentId, ErrorIssueId, + EscalationChainId, InvestigationId, McpConnectionId, MobileDeviceId, @@ -22,6 +23,7 @@ export const AlertDestinationPublicId = PublicId(PublicIdPrefixes.alertDestinati export const AlertIncidentPublicId = PublicId(PublicIdPrefixes.alertIncident, AlertIncidentId) export const AlertDeliveryEventPublicId = PublicId(PublicIdPrefixes.event, AlertDeliveryEventId) export const AlertIncidentEventPublicId = PublicId(PublicIdPrefixes.event, AlertIncidentEventId) +export const EscalationChainPublicId = PublicId(PublicIdPrefixes.escalationChain, EscalationChainId) export const ActorPublicId = PublicId(PublicIdPrefixes.actor, ActorId) export const AnomalyIncidentPublicId = PublicId(PublicIdPrefixes.anomalyIncident, AnomalyIncidentId) export const ErrorIncidentPublicId = PublicId(PublicIdPrefixes.errorIncident, ErrorIncidentId) diff --git a/packages/domain/src/http/v2/v2-contract.test.ts b/packages/domain/src/http/v2/v2-contract.test.ts index c7c4866226..0d97ae339e 100644 --- a/packages/domain/src/http/v2/v2-contract.test.ts +++ b/packages/domain/src/http/v2/v2-contract.test.ts @@ -7,6 +7,7 @@ import { V2AlertRule, V2AlertRuleMutationResponse } from "./alert-rules" import { V2ApiKey, V2ApiKeyMutationResponse, V2ApiKeyWithSecret } from "./api-keys" import { V2DashboardMutation } from "./dashboards" import { V2ErrorIssue } from "./error-issues" +import { V2EscalationChain } from "./escalation-chains" import { AuthorizationV2, isScopeExemptRoute, requiredScopeForRoute, scopeAllows, V2Scope } from "./auth" import { V2PlanetScaleIntegration, @@ -419,6 +420,60 @@ describe("V2 alerts wire format", () => { expect("txid" in Schema.encodeSync(V2AlertRule)(base)).toBe(false) }) + it("reads an omitted tier escalation chain as null and round-trips an esc_ id", () => { + const CHAIN_UUID = "3f2a1b4c-5d6e-4f70-8a9b-0c1d2e3f4a5b" + const without = Schema.decodeUnknownSync(V2AlertRule)(ruleWire) + expect(without.thresholds[0]?.escalation_chain_id).toBeNull() + + const [tier] = ruleWire.thresholds + const withChain = Schema.decodeUnknownSync(V2AlertRule)({ + ...ruleWire, + thresholds: [{ ...tier, escalation_chain_id: encodePublicId("esc", CHAIN_UUID) }], + }) + expect(withChain.thresholds[0]?.escalation_chain_id).toBe(CHAIN_UUID) + expect(Schema.encodeSync(V2AlertRule)(withChain).thresholds[0]?.escalation_chain_id).toBe( + encodePublicId("esc", CHAIN_UUID), + ) + expect(() => + Schema.decodeUnknownSync(V2AlertRule)({ + ...ruleWire, + thresholds: [{ ...tier, escalation_chain_id: encodePublicId("dest", CHAIN_UUID) }], + }), + ).toThrow() + }) + + it("encodes an escalation chain with esc_/dest_/alrt_ ids and snake_case steps", () => { + const CHAIN_UUID = "3f2a1b4c-5d6e-4f70-8a9b-0c1d2e3f4a5b" + const chainWire = { + id: encodePublicId("esc", CHAIN_UUID), + object: "escalation_chain", + name: "On-call", + description: null, + steps: [ + { + targets: [{ type: "destination", destination_id: encodePublicId("dest", DEST_UUID) }], + wait_minutes: 10, + }, + ], + repeat_count: 1, + rule_ids: [encodePublicId("alrt", UUID)], + created_at: "2026-07-15T00:00:00.000Z", + updated_at: "2026-07-15T00:00:00.000Z", + } + const chain = Schema.decodeUnknownSync(V2EscalationChain)(chainWire) + expect(chain.id).toBe(CHAIN_UUID) + expect(chain.steps[0]?.targets[0]?.destination_id).toBe(DEST_UUID) + expect(chain.rule_ids).toEqual([UUID]) + expect(Schema.encodeSync(V2EscalationChain)(chain)).toEqual(chainWire) + expect(() => Schema.decodeUnknownSync(V2EscalationChain)({ ...chainWire, steps: [] })).toThrow() + expect(() => + Schema.decodeUnknownSync(V2EscalationChain)({ + ...chainWire, + steps: [{ ...chainWire.steps[0], wait_minutes: 0 }], + }), + ).toThrow() + }) + it("rejects wrong-prefix rule IDs", () => { expect(() => Schema.decodeUnknownSync(V2AlertRule)({ ...ruleWire, id: encodePublicId("dash", UUID) }), diff --git a/packages/domain/src/mcp-outputs/alerts.ts b/packages/domain/src/mcp-outputs/alerts.ts index 17e5776ed7..3504f80d48 100644 --- a/packages/domain/src/mcp-outputs/alerts.ts +++ b/packages/domain/src/mcp-outputs/alerts.ts @@ -23,6 +23,16 @@ export const AlertRuleRow = Schema.Struct({ /** Set when the scheduler slowed the rule after repeated query timeouts; cleared by an edit. */ slowedIntervalMinutes: Schema.NullOr(Schema.Number), destinationIds: Schema.Array(Schema.String), + /** Escalation chains the rule's tiers start, when any do; `name` only where it was looked up. */ + escalationChains: Schema.optionalKey( + Schema.Array( + Schema.Struct({ + severity: Schema.String, + chainId: Schema.String, + name: Schema.optionalKey(NullableString), + }), + ), + ), createdAt: Schema.String, updatedAt: Schema.String, }) diff --git a/packages/domain/src/mcp-outputs/catalog.ts b/packages/domain/src/mcp-outputs/catalog.ts index e279057cef..11d0e3aa40 100644 --- a/packages/domain/src/mcp-outputs/catalog.ts +++ b/packages/domain/src/mcp-outputs/catalog.ts @@ -46,6 +46,11 @@ import { ListAlertRulesOutput, UpdateAlertRuleOutput, } from "./alerts" +import { + DeleteEscalationChainOutput, + EscalationChainMutationOutput, + ListEscalationChainsOutput, +} from "./escalation-chains" import { AddDashboardWidgetOutput, CreateDashboardOutput, @@ -140,6 +145,7 @@ export const McpToolOutputs = { compare_commits: CompareCommitsOutput, compare_periods: ComparePeriodsOutput, create_alert_rule: CreateAlertRuleOutput, + create_escalation_chain: EscalationChainMutationOutput, create_ingest_rule: IngestRuleMutationOutput, create_scrape_target: ScrapeTargetMutationOutput, create_dashboard: CreateDashboardOutput, @@ -149,6 +155,7 @@ export const McpToolOutputs = { delete_ingest_rule: DeleteIngestRuleOutput, delete_scrape_target: DeleteScrapeTargetOutput, delete_alert_rule: DeleteAlertRuleOutput, + delete_escalation_chain: DeleteEscalationChainOutput, acknowledge_alert_incident: AcknowledgeAlertIncidentOutput, delete_uptime_monitor: DeleteUptimeMonitorOutput, describe_dashboard_schema: DescribeDashboardSchemaOutput, @@ -186,6 +193,7 @@ export const McpToolOutputs = { list_alert_destinations: ListAlertDestinationsOutput, list_alert_incidents: ListAlertIncidentsOutput, list_alert_rules: ListAlertRulesOutput, + list_escalation_chains: ListEscalationChainsOutput, list_dashboards: ListDashboardsOutput, list_error_incidents: ListErrorIncidentsOutput, list_error_issue_events: ListErrorIssueEventsOutput, @@ -241,6 +249,7 @@ export const McpToolOutputs = { transition_error_issue: TransitionErrorIssueOutput, transition_error_issues: TransitionErrorIssuesOutput, update_alert_rule: UpdateAlertRuleOutput, + update_escalation_chain: EscalationChainMutationOutput, update_dashboard: UpdateDashboardOutput, update_dashboard_widget: UpdateDashboardWidgetOutput, update_ingest_rule: IngestRuleMutationOutput, diff --git a/packages/domain/src/mcp-outputs/escalation-chains.ts b/packages/domain/src/mcp-outputs/escalation-chains.ts new file mode 100644 index 0000000000..f36193ef1f --- /dev/null +++ b/packages/domain/src/mcp-outputs/escalation-chains.ts @@ -0,0 +1,39 @@ +/** Output schemas for the escalation chain MCP tools. */ +import { Schema } from "effect" + +const NullableString = Schema.NullOr(Schema.String) + +export const EscalationTargetRow = Schema.Struct({ + destinationId: Schema.String, + /** Null when the destination list could not be read or the destination is gone. */ + name: NullableString, + type: NullableString, +}) + +export const EscalationStepRow = Schema.Struct({ + targets: Schema.Array(EscalationTargetRow), + waitMinutes: Schema.Number, +}) + +export const EscalationChainRow = Schema.Struct({ + id: Schema.String, + /** The same chain's id in the REST API (`esc_…`). */ + publicId: Schema.String, + name: Schema.String, + description: NullableString, + steps: Schema.Array(EscalationStepRow), + repeatCount: Schema.Number, + /** Rules with a tier that starts this chain. */ + ruleIds: Schema.Array(Schema.String), + createdAt: Schema.String, + updatedAt: Schema.String, +}) + +export const ListEscalationChainsOutput = Schema.Struct({ + chains: Schema.Array(EscalationChainRow), + total: Schema.Number, +}) + +export const EscalationChainMutationOutput = Schema.Struct({ chain: EscalationChainRow }) + +export const DeleteEscalationChainOutput = Schema.Struct({ id: Schema.String, name: Schema.String }) diff --git a/packages/domain/src/mcp-outputs/index.ts b/packages/domain/src/mcp-outputs/index.ts index 121daed48f..f799fdd77c 100644 --- a/packages/domain/src/mcp-outputs/index.ts +++ b/packages/domain/src/mcp-outputs/index.ts @@ -4,6 +4,7 @@ export * from "./traces" export * from "./services" export * from "./sessions" export * from "./alerts" +export * from "./escalation-chains" export * from "./ingest-rules" export * from "./dashboards" export * from "./issues" diff --git a/packages/web-data/src/alerts/form-utils.test.ts b/packages/web-data/src/alerts/form-utils.test.ts index 63155df403..c3d20698ac 100644 --- a/packages/web-data/src/alerts/form-utils.test.ts +++ b/packages/web-data/src/alerts/form-utils.test.ts @@ -302,7 +302,14 @@ describe("v2 response mappers", () => { threshold: 0.05, threshold_upper: null, thresholds: [ - { severity: "warning", value: 0.05, upper: null, recovery_value: 0.03, destination_ids: [] }, + { + severity: "warning", + value: 0.05, + upper: null, + recovery_value: 0.03, + destination_ids: [], + escalation_chain_id: null, + }, ], comparator: "gt", truncated_to_start: "2026-07-15T00:00:00.000Z", @@ -331,7 +338,14 @@ describe("v2 response mappers", () => { }) expect(response.thresholds).toEqual([ - { severity: "warning", value: 0.05, upper: null, recoveryValue: 0.03, destinationIds: [] }, + { + severity: "warning", + value: 0.05, + upper: null, + recoveryValue: 0.03, + destinationIds: [], + escalationChainId: null, + }, ]) expect(response.bucketSeconds).toBe(300) expect(response.windowMinutes).toBe(5) @@ -361,7 +375,14 @@ describe("v2 response mappers", () => { threshold: 10, threshold_upper: null, thresholds: [ - { severity: "warning", value: 10, upper: null, recovery_value: null, destination_ids: [] }, + { + severity: "warning", + value: 10, + upper: null, + recovery_value: null, + destination_ids: [], + escalation_chain_id: null, + }, ], comparator: "gt", truncated_to_start: null, diff --git a/packages/web-data/src/alerts/form-utils.ts b/packages/web-data/src/alerts/form-utils.ts index f87216b077..c9541f927f 100644 --- a/packages/web-data/src/alerts/form-utils.ts +++ b/packages/web-data/src/alerts/form-utils.ts @@ -16,6 +16,7 @@ import { type AlertComparator, type AlertDeliveryStatus, type AlertDestinationId, + type EscalationChainId, type AlertDestinationType, type AlertEventType, type AlertSeverity, @@ -105,6 +106,8 @@ export type RuleFormState = { rawQuerySql: string rawQueryReducer: QueryEngineAlertReducer destinationIds: AlertDestinationId[] + /** The escalation chain the main tier starts; null when it only notifies its destinations. */ + escalationChainId: EscalationChainId | null /** * Custom notification message. Empty strings mean "use the built-in format". * `title` + Markdown `body` support `{{ variable }}` substitution; channels @@ -119,6 +122,7 @@ export interface EscalationTierForm { threshold: string recoveryValue: string destinationIds: AlertDestinationId[] + escalationChainId: EscalationChainId | null } export const signalLabels: Record = { @@ -327,6 +331,7 @@ export function defaultRuleForm(serviceName?: string): RuleFormState { rawQuerySql: DEFAULT_RAW_QUERY_SQL, rawQueryReducer: "identity", destinationIds: [], + escalationChainId: null, notificationTitle: "", notificationBody: "", } @@ -369,6 +374,7 @@ export function ruleToFormState(rule: AlertRuleDocument): RuleFormState { threshold: toForm(escalation.value), recoveryValue: toForm(escalation.recoveryValue), destinationIds: [...escalation.destinationIds], + escalationChainId: escalation.escalationChainId ?? null, }, windowMinutes: String(rule.windowMinutes), evaluationIntervalMinutes: @@ -384,6 +390,7 @@ export function ruleToFormState(rule: AlertRuleDocument): RuleFormState { rawQuerySql: rule.rawQuerySql ?? DEFAULT_RAW_QUERY_SQL, rawQueryReducer: rule.rawQueryReducer ?? "identity", destinationIds: [...primary.destinationIds], + escalationChainId: primary.escalationChainId ?? null, notificationTitle: rule.notificationTemplate?.title ?? "", notificationBody: rule.notificationTemplate?.body ?? "", } @@ -503,6 +510,7 @@ export function formThresholds(form: RuleFormState): Arr.NonEmptyReadonlyArray Date: Fri, 6 Nov 2026 13:12:40 +0100 Subject: [PATCH 13/29] fix(alerts): use Arr.findFirst for the delivery log's connector lookup Same change as #2302, so CI's effect lint passes on this stack before it lands. --- apps/web/src/page/alerts/hub/view-routing.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/web/src/page/alerts/hub/view-routing.ts b/apps/web/src/page/alerts/hub/view-routing.ts index 5c2fd2f020..64592dcc23 100644 --- a/apps/web/src/page/alerts/hub/view-routing.ts +++ b/apps/web/src/page/alerts/hub/view-routing.ts @@ -456,7 +456,12 @@ const logRow = (h: H, model: Model, inputs: RoutingInputs, event: AlertDeliveryE const ev = eventTypeMeta[event.eventType] const status = deliveryStatusMeta[event.status] // Delivery events carry only the type; a `chat` destination's logo comes from its connector. - const chatConnector = inputs.destinations.find((d) => d.id === event.destinationId)?.chatConnector + const chatConnector = Option.getOrUndefined( + Option.flatMapNullishOr( + Arr.findFirst(inputs.destinations, (d) => d.id === event.destinationId), + (d) => d.chatConnector, + ), + ) return tableRow(h, [ tableCell(h, [ hstack(h, { gap: "1.5", as: "span" }, [ From 16ae3907de667c56b1c4d3dadae22a1db8791fdc Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 13:26:06 +0100 Subject: [PATCH 14/29] fix(alerts): escalation runs survive flapping, stop on de-escalation, and lose to a resolve or ack - the scheduler reads only runs with a decision to make, so standing acks cannot crowd out due steps - a step locks its incident and the drain drops a step whose incident closed or was acknowledged - a flap-suppressed re-open starts the chain past step 1 instead of dropping it - de-escalation stops the critical tier's chain; escalation refreshes a running chain's payload - v2 PATCH tiers without escalation_chain_id keep the stored chain; MCP "none" detaches every tier --- apps/ai/src/mcp/tool-descriptors.generated.ts | 2 +- apps/ai/src/mcp/tools/update-alert-rule.ts | 4 +- apps/api/src/routes/v2/alert-rules.http.ts | 18 +- apps/api/src/routes/v2/alerts.http.test.ts | 7 + .../MapleAPI/Sources/MapleAPI/openapi.json | 2 +- .../alerts/AlertsService.escalation.test.ts | 157 ++++++++++++++++-- .../src/services/alerts/AlertsService.ts | 121 ++++++++++---- .../alerts/EscalationChainsService.ts | 8 +- .../services/alerts/alert-escalation-runs.ts | 109 +++++++++++- packages/domain/src/http/v2/alert-rules.ts | 13 +- .../domain/src/http/v2/v2-contract.test.ts | 9 +- 11 files changed, 383 insertions(+), 67 deletions(-) diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 8676547b41..6004aa6db4 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -35,7 +35,7 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations). escalation_chain_id adds step-by-step paging on top.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, - {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or \"none\" to detach it. On a rule with warning and critical tiers it sets the critical tier: its UUID or its REST id (esc_\u2026)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or \"none\" to detach it from every tier. On a rule with warning and critical tiers an id sets the critical tier: its UUID or its REST id (esc_\u2026)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_alert_rule","title":"Delete Alert Rule","description":"Permanently delete an alert rule together with its incident history, delivery events and evaluation state. Irreversible; requires confirm=true. Ids from list_alert_rules.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"confirm":{"type":"boolean","description":"Must be true. There is no undo."}},"required":["rule_id","confirm"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"annotations":{"title":"Delete Alert Rule","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleId","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/update-alert-rule.ts b/apps/ai/src/mcp/tools/update-alert-rule.ts index 7101584acb..62752b9e28 100644 --- a/apps/ai/src/mcp/tools/update-alert-rule.ts +++ b/apps/ai/src/mcp/tools/update-alert-rule.ts @@ -105,7 +105,7 @@ export const UpdateAlertRuleParameters = Schema.Struct({ ), escalation_chain_id: P.optionalResourceId( "escalationChain", - 'Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or "none" to detach it. On a rule with warning and critical tiers it sets the critical tier', + 'Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or "none" to detach it from every tier. On a rule with warning and critical tiers an id sets the critical tier', ), }) @@ -246,6 +246,8 @@ const withEscalationChain = => { if (chain === undefined) return tiers + // "none" detaches every tier's chain, wherever it was set. + if (chain === null) return Arr.map(tiers, (tier) => ({ ...tier, escalationChainId: null })) const critical = Arr.findLastIndex(tiers, (tier) => tier.severity === "critical") const target = tiers.length === 1 ? 0 : Option.getOrElse(critical, () => tiers.length - 1) return Arr.map(tiers, (tier, index) => (index === target ? { ...tier, escalationChainId: chain } : tier)) diff --git a/apps/api/src/routes/v2/alert-rules.http.ts b/apps/api/src/routes/v2/alert-rules.http.ts index 5b24ec59a0..7e10fa3015 100644 --- a/apps/api/src/routes/v2/alert-rules.http.ts +++ b/apps/api/src/routes/v2/alert-rules.http.ts @@ -282,7 +282,23 @@ const mergeThresholds = ( const fitBound = (tier: AlertThreshold): AlertThreshold => isRangeComparator(comparator) ? tier : { ...tier, upper: null } if (patch.thresholds !== undefined) { - return Effect.succeed(Arr.map(patch.thresholds, fromV2Threshold)) + // A tier that leaves out its chain keeps the one stored at the same severity. + return Effect.succeed( + Arr.map(patch.thresholds, (tier) => + tier.escalation_chain_id !== undefined + ? fromV2Threshold(tier) + : { + ...fromV2Threshold(tier), + escalationChainId: Arr.findFirst( + doc.thresholds, + (stored) => stored.severity === tier.severity, + ).pipe( + Option.flatMapNullishOr((stored) => stored.escalationChainId), + Option.getOrNull, + ), + }, + ), + ) } // Legacy fields echoed back as read (a client PATCHing the document it fetched) are not edits. const echoed = { diff --git a/apps/api/src/routes/v2/alerts.http.test.ts b/apps/api/src/routes/v2/alerts.http.test.ts index e5201d1af8..f5a9105fce 100644 --- a/apps/api/src/routes/v2/alerts.http.test.ts +++ b/apps/api/src/routes/v2/alerts.http.test.ts @@ -1381,6 +1381,13 @@ describe("v2 alerts over HTTP", () => { expect(inUse.status).toBe(409) expect(inUse.body.error.code).toBe("escalation_chain_in_use") + // A client that predates chains edits the tiers without the field: the chain stays. + const retuned = await harness.request("PATCH", `/v2/alerts/rules/${rule.body.id}`, key.secret, { + thresholds: [{ severity: "critical", value: 0.06, destination_ids: [primary.body.id] }], + }) + expect(retuned.status, JSON.stringify(retuned.body)).toBe(200) + expect(retuned.body.thresholds[0]).toMatchObject({ value: 0.06, escalation_chain_id: chainId }) + const detached = await harness.request("PATCH", `/v2/alerts/rules/${rule.body.id}`, key.secret, { thresholds: [ { diff --git a/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json b/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json index 3431531215..fc159de7e1 100644 --- a/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json +++ b/apps/ios/Packages/MapleAPI/Sources/MapleAPI/openapi.json @@ -1421,7 +1421,7 @@ }, "escalation_chain_id": { "$ref": "#/components/schemas/_maple_EscalationChainId", - "description": "Escalation chain (`esc_…`) an incident at this severity starts, on top of `destination_ids`, or `null` (the default) for none. It pages step by step until the incident is acknowledged or resolved." + "description": "Escalation chain (`esc_…`) an incident at this severity starts, on top of `destination_ids`, or `null` for none. It pages step by step until the incident is acknowledged or resolved. Omitted on create means none; omitted on update keeps the tier's current chain." }, "recovery_value": { "description": "The value the signal must cross back over before this tier clears, or `null` (the default) to clear as soon as it stops breaching. Must sit on the healthy side of `value`.", diff --git a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts index 1c5b0b86a9..c1d2d28314 100644 --- a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts +++ b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts @@ -15,7 +15,9 @@ import { afterEach, assert, describe, it } from "@effect/vitest" import { Duration, Effect, Layer, Schema, Array as Arr, String as Str } from "effect" import { TestClock } from "effect/testing" import { cleanupTestDbs, createTestDb, executeSql, type TestDb } from "@maple/backend/platform/test-pglite" +import { Database } from "@maple/backend/platform/DatabaseLive" import { AlertsService, type AlertsServiceApi } from "./AlertsService" +import { applyEscalationPlan, planEscalationWork, selectEscalationWork } from "./alert-escalation-runs" import { EscalationChainsService } from "./EscalationChainsService" import { DEFAULT_CLOCK_EPOCH_MS, @@ -64,6 +66,7 @@ const layerFor = ( Layer.mergeAll( makeLayer(testDb, makeWarehouseStub(state), { fetch: recordingFetch(requests) }), EscalationChainsService.layer.pipe(Layer.provide(testDb.layer)), + testDb.layer, ) const tick = (alerts: AlertsServiceApi, minutes = 1) => @@ -253,30 +256,162 @@ describe("escalation chains", () => { }).pipe(Effect.provide(layerFor(testDb, state, requests))) }) - it.effect("sends a due step once when two scheduler ticks overlap", () => { + it.effect("moves a step only from the position it was read at, so a second writer loses", () => { const testDb = createTestDb(trackedDbs) const state = { tracesAggregateRows: errorRateRows(10) } const requests: Array = [] return Effect.gen(function* () { yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) - const { alerts, orgId } = yield* setup("org_esc_race") + const { alerts } = yield* setup("org_esc_cas") + const database = yield* Database yield* alerts.runSchedulerTick() yield* tick(alerts) yield* ticks(alerts, 9) yield* TestClock.adjust(Duration.minutes(1)) - yield* Effect.all([alerts.runSchedulerTick(), alerts.runSchedulerTick()], { concurrency: 2 }) - assert.deepStrictEqual( - Arr.filter(requests, (request) => request === "trigger pager"), - ["trigger pager"], + const nowMs = DEFAULT_CLOCK_EPOCH_MS + 11 * 60_000 + // Both writers read the run at the same position, as two overlapping ticks would. + const [row] = yield* database.execute((db) => selectEscalationWork(db, nowMs)) + if (row === undefined) return assert.fail("expected a due run") + const plan = planEscalationWork(row, false, nowMs) + const apply = database.execute((db) => + db.transaction( + applyEscalationPlan(db, { + row, + plan, + nowMs, + enabled: new Set(), + makeDeliveryId: crypto.randomUUID.bind(crypto), + }), + ), ) + assert.deepStrictEqual([yield* apply, yield* apply], ["sent", "lost"]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("reads only runs with a decision to make, so standing acks cannot crowd out a due step", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId } = yield* setup("org_esc_starve") + const database = yield* Database + yield* alerts.runSchedulerTick() + yield* tick(alerts) + yield* setAck(testDb, orgId, true) + // Read once it falls due, and paused there. + yield* ticks(alerts, 12) + assert.strictEqual((yield* runRows(testDb, orgId))[0]?.status, "acknowledged") + // Paused on a standing ack: nothing to decide, however long it has been due. + const nowMs = DEFAULT_CLOCK_EPOCH_MS + 120 * 60_000 + assert.lengthOf(yield* database.execute((db) => selectEscalationWork(db, nowMs, 1)), 0) + yield* setAck(testDb, orgId, false) + assert.lengthOf(yield* database.execute((db) => selectEscalationWork(db, nowMs, 1)), 1) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("does not send a queued step once the incident is acknowledged", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId, pager } = yield* setup("org_esc_drain") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + // A step queued just before an ack committed, as the drain would find it. + yield* Effect.promise(() => + executeSql( + testDb, + `insert into alert_delivery_events (id, org_id, incident_id, rule_id, destination_id, delivery_key, + event_type, attempt_number, status, scheduled_at, payload_json, created_at, updated_at) + select gen_random_uuid()::text, org_id, incident_id, rule_id, $2, delivery_key || ':late', event_type, 1, + 'queued', scheduled_at, payload_json || '{"escalation":{"step":2}}'::jsonb, created_at, updated_at + from alert_delivery_events where org_id = $1 limit 1`, + [orgId, pager.id], + ), + ) + yield* setAck(testDb, orgId, true) + yield* tick(alerts) + assert.notInclude(requests, "trigger pager") const rows = yield* Effect.promise(() => - testDb.pglite.query<{ n: number }>( - `select count(*)::int as n from alert_delivery_events d join alert_destinations x on x.id = d.destination_id - where d.org_id = $1 and x.name = 'pager'`, - [orgId], + testDb.pglite.query<{ status: string; error_message: string }>( + "select status, error_message from alert_delivery_events where org_id = $1 and destination_id = $2", + [orgId, pager.id], ), ) - assert.strictEqual(rows.rows[0]?.n, 1) + assert.strictEqual(rows.rows[0]?.status, "failed") + assert.include(rows.rows[0]?.error_message ?? "", "acknowledged") + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("keeps escalating after a flap-suppressed re-open, past the step already paged", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId } = yield* setup("org_esc_flap") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + // Resolves, then re-opens inside the reminder interval: the re-open pages nobody. + state.tracesAggregateRows = errorRateRows(1) + yield* ticks(alerts, 2) + state.tracesAggregateRows = errorRateRows(10) + yield* ticks(alerts, 2) + assert.sameMembers(requests, ["trigger team", "trigger slack", "resolve team", "resolve slack"]) + // The chain carries on from step 2 for the new incident. + yield* ticks(alerts, 10) + assert.include(requests, "trigger pager") + assert.lengthOf( + Arr.filter(requests, (request) => request === "trigger slack"), + 1, + ) + assert.sameMembers( + Arr.map(yield* runRows(testDb, orgId), (row) => row.status), + ["resolved", "exhausted"], + ) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect("stops the critical tier's chain when the incident drops back to warning", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(30) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const alerts = yield* AlertsService + const chains = yield* EscalationChainsService + const orgId = asOrgId("org_esc_deescalate") + const userId = asUserId("user_esc_deescalate") + const team = yield* webhook(alerts, orgId, userId, "team") + const pager = yield* webhook(alerts, orgId, userId, "pager") + const chain = yield* chains.create( + orgId, + userId, + adminRoles, + new EscalationChainCreateRequest({ + name: "Page chain", + steps: [{ targets: [{ type: "destination", destinationId: pager.id }], waitMinutes: 10 }], + repeatCount: 3, + }), + ) + yield* alerts.createRule( + orgId, + userId, + adminRoles, + ruleRequest([tier("critical", 20, [], chain.id), tier("warning", 5, [team.id], null)]), + ) + yield* alerts.runSchedulerTick() + yield* tick(alerts) + assert.include(requests, "trigger pager") + state.tracesAggregateRows = errorRateRows(10) + yield* ticks(alerts, 30) + assert.lengthOf( + Arr.filter(requests, (request) => request === "trigger pager"), + 1, + ) + assert.strictEqual((yield* runRows(testDb, orgId))[0]?.status, "superseded") }).pipe(Effect.provide(layerFor(testDb, state, requests))) }) diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index e6294a2ba0..fdb8639156 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -148,8 +148,10 @@ import { applyEscalationPlan, enabledDestinationIds, type EscalationWorkRow, + endEscalationRunsAbove, loadEscalationChain, planEscalationWork, + refreshEscalationRunPayload, selectEscalationWork, startEscalationRun, } from "./alert-escalation-runs" @@ -435,6 +437,8 @@ const StoredDeliveryPayloadSchema = Schema.StructWithRest( }), ), template: Schema.optionalKey(Schema.NullOr(AlertNotificationTemplate)), + /** Set on an escalation step's trigger. */ + escalation: Schema.optionalKey(Schema.Unknown), chart: Schema.optionalKey( Schema.NullOr( Schema.Struct({ @@ -1622,15 +1626,19 @@ export class AlertsService extends Context.Service, ) { - const deliveries = yield* prepareIncidentDeliveries( - orgId, - rule, - incident, - evaluation, - eventType, - scheduledAt, - ) + const deliveries = + prepared ?? + (yield* prepareIncidentDeliveries( + orgId, + rule, + incident, + evaluation, + eventType, + scheduledAt, + )) if (deliveries.length > 0) yield* dbExecute((db) => insertIncidentDeliveries(db, deliveries)) yield* pushIncidentNotification( @@ -2493,6 +2501,24 @@ export class AlertsService extends Context.Service @@ -3451,8 +3494,17 @@ export class AlertsService extends Context.Service [$.id.eq(openIncident.id), $.status.eq("open")]) .returning("id"), ) - if (updated.length === 0 || lifecycle.transition !== "escalated") - return updated + if (updated.length === 0) return updated + // Back down a tier, the higher tier's chain stops paging its on-call. + if (lifecycle.transition === "deescalated") { + yield* endEscalationRunsAbove(db, { + orgId: row.orgId, + incidentId: openIncident.id, + severity, + nowMs: timestamp, + }) + } + if (lifecycle.transition !== "escalated") return updated if (escalation !== null) { yield* startEscalationRun(db, { id: decodeAlertEscalationRunIdSync(makeUuid()), @@ -3461,6 +3513,13 @@ export class AlertsService extends Context.Service - db.execute(Orm.sql`select pg_advisory_xact_lock(hashtext(${orgId}))`) + // The org's advisory lock: rule writes and destination deletion take it around their reference checks. + const lockOrg = lockOrgAlertRules const requireDestinations = (db: MapleDb, orgId: OrgId, steps: EscalationSteps) => Effect.gen(function* () { diff --git a/packages/backend/src/services/alerts/alert-escalation-runs.ts b/packages/backend/src/services/alerts/alert-escalation-runs.ts index 450e941b9a..eeb6e25ff1 100644 --- a/packages/backend/src/services/alerts/alert-escalation-runs.ts +++ b/packages/backend/src/services/alerts/alert-escalation-runs.ts @@ -70,6 +70,8 @@ export interface StartEscalationRunInput { /** The incident's trigger notification; each step resends it to the step's targets. */ readonly payload: Record readonly nowMs: number + /** Start past step 1, which a flap-suppressed re-open's predecessor already paged. */ + readonly firstStepSent?: boolean } /** @@ -79,6 +81,7 @@ export interface StartEscalationRunInput { */ export const startEscalationRun = (db: MapleDb, input: StartEscalationRunInput) => Effect.gen(function* () { + const position = startPosition(input) yield* db.run( PG.update(AlertEscalationRuns) .set({ status: "superseded", endedAt: input.nowMs, updatedAt: input.nowMs }) @@ -88,6 +91,7 @@ export const startEscalationRun = (db: MapleDb, input: StartEscalationRunInput) $.status.in_(...LIVE_STATUSES), ]), ) + if (position === null) return yield* db.run( PG.insertInto(AlertEscalationRuns).values({ id: input.id, @@ -98,9 +102,9 @@ export const startEscalationRun = (db: MapleDb, input: StartEscalationRunInput) severity: input.severity, stepsJson: input.chain.steps, repeatCount: input.chain.repeatCount, - stepIndex: 0, - passIndex: 0, - nextStepAt: input.nowMs, + stepIndex: position.stepIndex, + passIndex: position.passIndex, + nextStepAt: position.nextStepAt, status: "active", payloadJson: input.payload, stepsSent: 0, @@ -112,11 +116,71 @@ export const startEscalationRun = (db: MapleDb, input: StartEscalationRunInput) ) }) +/** Where a new run starts: step 1 now, or the step after it; null when step 1 was the whole chain. */ +const startPosition = (input: StartEscalationRunInput) => { + const first = { stepIndex: 0, passIndex: 0, nextStepAt: input.nowMs } + if (input.firstStepSent !== true) return first + const plan = planEscalationAdvance( + { status: "active", steps: input.chain.steps, repeatCount: input.chain.repeatCount, ...first }, + { open: true, acknowledged: false, silenced: false }, + input.nowMs, + ) + return plan.kind === "send" && plan.next.kind === "step" ? plan.next : null +} + +/** Stops the incident's runs started by a tier above `severity`, on a de-escalation. */ +export const endEscalationRunsAbove = ( + db: MapleDb, + input: { + readonly orgId: OrgId + readonly incidentId: AlertIncidentId + readonly severity: AlertSeverity + readonly nowMs: number + }, +) => + // Two tiers: only a warning incident has a tier above it. + input.severity !== "warning" + ? Effect.void + : db + .run( + PG.update(AlertEscalationRuns) + .set({ status: "superseded", endedAt: input.nowMs, updatedAt: input.nowMs }) + .where(($) => [ + $.orgId.eq(input.orgId), + $.incidentId.eq(input.incidentId), + $.status.in_(...LIVE_STATUSES), + $.severity.eq("critical"), + ]), + ) + .pipe(Effect.asVoid) + +/** The running chain's next steps carry the incident's new trigger (its severity), on an escalation. */ +export const refreshEscalationRunPayload = ( + db: MapleDb, + input: { + readonly orgId: OrgId + readonly incidentId: AlertIncidentId + readonly payload: Record + readonly nowMs: number + }, +) => + db.run( + PG.update(AlertEscalationRuns) + .set({ payloadJson: input.payload, updatedAt: input.nowMs }) + .where(($) => [ + $.orgId.eq(input.orgId), + $.incidentId.eq(input.incidentId), + $.status.in_(...LIVE_STATUSES), + ]), + ) + /** - * Live runs with something to decide: due, paused on an ack that may be gone, or on an incident - * that is no longer open. Across orgs: the scheduler is the system actor. + * Live runs with something to decide: an active run that is due or whose incident closed, and a + * paused run whose ack is gone or whose incident closed. A run paused on a standing ack is not + * read, so long acknowledged incidents cannot crowd out due ones. Across orgs: the scheduler is + * the system actor. */ -export const selectEscalationWork = (db: MapleDb, nowMs: number) => +export const selectEscalationWork = (db: MapleDb, nowMs: number, limit = ESCALATION_RUNS_PER_TICK) => db.run( PG.from(AlertEscalationRuns, "r") .innerJoin(AlertIncidents, "i", (r, i) => r.incidentId.eq(i.id).and(r.orgId.eq(i.orgId))) @@ -142,10 +206,14 @@ export const selectEscalationWork = (db: MapleDb, nowMs: number) => })) .where(($) => [ $.status.in_(...LIVE_STATUSES), - PG.or($.nextStepAt.lte(nowMs), $.status.eq("acknowledged"), $.i.status.neq("open")), + PG.or( + $.i.status.neq("open"), + $.status.eq("active").and($.nextStepAt.lte(nowMs)), + $.status.eq("acknowledged").and($.i.acknowledgedAt.isNull()), + ), ]) .orderBy(["nextStepAt", "asc"]) - .limit(ESCALATION_RUNS_PER_TICK), + .limit(limit), ) export type EscalationWorkRow = Effect.Success>[number] @@ -221,6 +289,20 @@ export const applyEscalationPlan = (db: MapleDb, input: ApplyEscalationPlanInput case "defer": return (yield* write({ status: "active", nextStepAt: plan.nextStepAt })) ? "deferred" : "lost" case "send": { + // Locks the incident so a resolve or ack serializes with this step: once either has + // committed, the step is not sent and the next tick ends or pauses the run. + const firing = yield* db.run( + PG.from(AlertIncidents) + .select("id") + .where(($) => [ + $.id.eq(row.incidentId), + $.orgId.eq(row.orgId), + $.status.eq("open"), + $.acknowledgedAt.isNull(), + ]) + .forUpdate(), + ) + if (firing.length === 0) return "lost" satisfies EscalationApplyOutcome const moved = yield* write( plan.next.kind === "exhausted" ? { @@ -257,6 +339,17 @@ export const applyEscalationPlan = (db: MapleDb, input: ApplyEscalationPlanInput (id) => input.enabled.has(id), ), ) + if (destinationIds.length === 0) { + yield* Effect.logWarning( + "Escalation step has no enabled destination; nobody was notified", + ).pipe( + Effect.annotateLogs({ + runId: row.id, + incidentId: row.incidentId, + step: plan.stepIndex + 1, + }), + ) + } yield* Effect.forEach( destinationIds, (destinationId) => diff --git a/packages/domain/src/http/v2/alert-rules.ts b/packages/domain/src/http/v2/alert-rules.ts index 8b0650879f..558d4b8ab4 100644 --- a/packages/domain/src/http/v2/alert-rules.ts +++ b/packages/domain/src/http/v2/alert-rules.ts @@ -73,12 +73,13 @@ export const V2AlertThreshold = Schema.Struct({ description: "Destinations (`dest_…`) this severity notifies. A `critical` incident also notifies every `warning` destination.", }), - escalation_chain_id: Schema.NullOr(EscalationChainPublicId) - .annotate({ + // Optional rather than defaulted: a PATCH tier without it keeps the stored chain. + escalation_chain_id: Schema.optionalKey( + Schema.NullOr(EscalationChainPublicId).annotate({ description: - "Escalation chain (`esc_…`) an incident at this severity starts, on top of `destination_ids`, or `null` (the default) for none. It pages step by step until the incident is acknowledged or resolved.", - }) - .pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))), + "Escalation chain (`esc_…`) an incident at this severity starts, on top of `destination_ids`, or `null` for none. It pages step by step until the incident is acknowledged or resolved. Omitted on create means none; omitted on update keeps the tier's current chain.", + }), + ), }).annotate({ identifier: "AlertThreshold", title: "Alert threshold", @@ -99,7 +100,7 @@ export const fromV2Threshold = (tier: V2AlertThreshold): AlertThreshold => ({ upper: tier.upper, recoveryValue: tier.recovery_value, destinationIds: tier.destination_ids, - escalationChainId: tier.escalation_chain_id, + escalationChainId: tier.escalation_chain_id ?? null, }) export const toV2Threshold = (tier: AlertThreshold): V2AlertThreshold => ({ diff --git a/packages/domain/src/http/v2/v2-contract.test.ts b/packages/domain/src/http/v2/v2-contract.test.ts index 0d97ae339e..4a5b278bfc 100644 --- a/packages/domain/src/http/v2/v2-contract.test.ts +++ b/packages/domain/src/http/v2/v2-contract.test.ts @@ -3,7 +3,7 @@ import { Effect, Predicate, Result, Schema, Array as Arr, Record as Rec, String import { MapleApiV2 } from "./api" import { V2AlertDestinationCreateParams } from "./alert-destinations" import { V2AlertIncident } from "./alert-incidents" -import { V2AlertRule, V2AlertRuleMutationResponse } from "./alert-rules" +import { fromV2Threshold, V2AlertRule, V2AlertRuleMutationResponse } from "./alert-rules" import { V2ApiKey, V2ApiKeyMutationResponse, V2ApiKeyWithSecret } from "./api-keys" import { V2DashboardMutation } from "./dashboards" import { V2ErrorIssue } from "./error-issues" @@ -420,10 +420,13 @@ describe("V2 alerts wire format", () => { expect("txid" in Schema.encodeSync(V2AlertRule)(base)).toBe(false) }) - it("reads an omitted tier escalation chain as null and round-trips an esc_ id", () => { + it("keeps an omitted tier escalation chain absent, reads it as no chain, and round-trips an esc_ id", () => { const CHAIN_UUID = "3f2a1b4c-5d6e-4f70-8a9b-0c1d2e3f4a5b" const without = Schema.decodeUnknownSync(V2AlertRule)(ruleWire) - expect(without.thresholds[0]?.escalation_chain_id).toBeNull() + // Absent, not null: an update that leaves it out keeps the stored chain. + const [omitted] = without.thresholds + expect(omitted).not.toHaveProperty("escalation_chain_id") + expect(fromV2Threshold(omitted).escalationChainId).toBeNull() const [tier] = ruleWire.thresholds const withChain = Schema.decodeUnknownSync(V2AlertRule)({ From 2e030b110ab3048a4e52016cd9cdb788536eff61 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 13:38:28 +0100 Subject: [PATCH 15/29] fix(web): a tier routed only by its escalation chain saves from the rule editor --- apps/web/src/page/alerts/editor/derive.ts | 2 +- apps/web/src/page/alerts/editor/view.ts | 3 +- .../web-data/src/alerts/form-utils.test.ts | 33 +++++++++++++++++++ packages/web-data/src/alerts/form-utils.ts | 5 ++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/apps/web/src/page/alerts/editor/derive.ts b/apps/web/src/page/alerts/editor/derive.ts index 60f4b78901..0832e80a93 100644 --- a/apps/web/src/page/alerts/editor/derive.ts +++ b/apps/web/src/page/alerts/editor/derive.ts @@ -106,7 +106,7 @@ export const validationIssues = (form: RuleForm): ReadonlyArray => { const intervalIssue = intervalWindowIssue(form) if (intervalIssue !== null) issues.push(intervalIssue) issues.push(...deriveRuleQueryIssues(form), ...deriveTierIssues(form)) - if (form.destinationIds.length === 0) issues.push("Who gets notified") + if (form.destinationIds.length === 0 && form.escalationChainId === null) issues.push("Who gets notified") return issues } diff --git a/apps/web/src/page/alerts/editor/view.ts b/apps/web/src/page/alerts/editor/view.ts index 0d84dc5904..19100c6c88 100644 --- a/apps/web/src/page/alerts/editor/view.ts +++ b/apps/web/src/page/alerts/editor/view.ts @@ -110,7 +110,8 @@ const evaluatedFormOf = stableSlice((form: RuleForm): RuleForm => ({ const surface = (h: H, model: Model, form: RuleForm, timeZone: string): ReadonlyArray => { const evaluated = evaluatedFormOf(form) const isNotifyMissing = - form.destinationIds.length === 0 || deriveTierIssues(form).includes("Critical destinations") + (form.destinationIds.length === 0 && form.escalationChainId === null) || + deriveTierIssues(form).includes("Critical destinations") return [ h.div( [h.Class(cn("mx-auto w-full space-y-10", RULE_FORM_MAX_WIDTH))], diff --git a/packages/web-data/src/alerts/form-utils.test.ts b/packages/web-data/src/alerts/form-utils.test.ts index c3d20698ac..4eab042c41 100644 --- a/packages/web-data/src/alerts/form-utils.test.ts +++ b/packages/web-data/src/alerts/form-utils.test.ts @@ -7,6 +7,7 @@ import { AlertIncidentId, AlertRuleId, ChatWorkspaceId, + EscalationChainId, } from "@maple/domain/http" import { buildDestinationCreateParamsV2, @@ -16,6 +17,8 @@ import { defaultDestinationForm, destinationToFormState, defaultRuleForm, + deriveTierIssues, + formThresholds, ruleFormIsGrouped, deriveRuleQueryIssues, domainThresholdToForm, @@ -638,3 +641,33 @@ describe("microsoft teams destination params", () => { ).toEqual({ type: "microsoft-teams", enabled: true, name: "Ops" }) }) }) + +describe("escalation chains on tiers", () => { + const warnChain = Schema.decodeSync(EscalationChainId)("3f2a1b4c-5d6e-4f70-8a9b-0c1d2e3f4a5b") + const pageChain = Schema.decodeSync(EscalationChainId)("9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d") + const twoTiers = { + ...defaultRuleForm(), + escalationChainId: warnChain, + escalation: { threshold: "10", recoveryValue: "", destinationIds: [], escalationChainId: pageChain }, + } + + it("accepts a critical tier routed only by its chain", () => { + expect(deriveTierIssues(twoTiers)).not.toContain("Critical destinations") + expect( + deriveTierIssues({ + ...twoTiers, + escalation: { ...twoTiers.escalation, escalationChainId: null }, + }), + ).toContain("Critical destinations") + }) + + it("saves each tier's chain on that tier", () => { + const [warning, critical] = formThresholds(twoTiers) + expect(warning).toMatchObject({ severity: "warning", escalation_chain_id: warnChain }) + expect(critical).toMatchObject({ + severity: "critical", + escalation_chain_id: pageChain, + destination_ids: [], + }) + }) +}) diff --git a/packages/web-data/src/alerts/form-utils.ts b/packages/web-data/src/alerts/form-utils.ts index 97af26bec9..6cbfe78910 100644 --- a/packages/web-data/src/alerts/form-utils.ts +++ b/packages/web-data/src/alerts/form-utils.ts @@ -492,7 +492,10 @@ export function deriveTierIssues(form: RuleFormState): string[] { issues.push("Critical recovery past warning") } recoveryIssue("Critical recovery value", escalation.threshold, escalation.recoveryValue) - if (escalation.destinationIds.length === 0) issues.push("Critical destinations") + // An escalation chain routes the tier on its own, as on the server. + if (escalation.destinationIds.length === 0 && escalation.escalationChainId === null) { + issues.push("Critical destinations") + } } if (Str.trim(form.breachesOutOf).length > 0) { const outOf = Number(form.breachesOutOf) From 21f887cb962c2c5dbad0bfec44d739cbe91fa986 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 13:48:06 +0100 Subject: [PATCH 16/29] test(web): a stored tier's escalation chain survives ruleToFormState and formThresholds --- .../web-data/src/alerts/form-utils.test.ts | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/packages/web-data/src/alerts/form-utils.test.ts b/packages/web-data/src/alerts/form-utils.test.ts index 4eab042c41..59d82ddfee 100644 --- a/packages/web-data/src/alerts/form-utils.test.ts +++ b/packages/web-data/src/alerts/form-utils.test.ts @@ -5,6 +5,7 @@ import { AlertDestinationDocument, AlertDestinationId, AlertIncidentId, + AlertRuleDocument, AlertRuleId, ChatWorkspaceId, EscalationChainId, @@ -20,6 +21,7 @@ import { deriveTierIssues, formThresholds, ruleFormIsGrouped, + ruleToFormState, deriveRuleQueryIssues, domainThresholdToForm, formThresholdToDomain, @@ -661,6 +663,72 @@ describe("escalation chains on tiers", () => { ).toContain("Critical destinations") }) + it("carries each stored tier's chain through the form unchanged", () => { + const iso = "2026-11-06T12:00:00.000Z" + const tiers = [ + { + severity: "warning", + value: 2, + upper: null, + recoveryValue: null, + destinationIds: [], + escalationChainId: warnChain, + }, + { + severity: "critical", + value: 5, + upper: null, + recoveryValue: null, + destinationIds: [], + escalationChainId: pageChain, + }, + ] + const rule = Schema.decodeUnknownSync(AlertRuleDocument)({ + id: "8d7c6b5a-4f3e-4d2c-9b1a-0f9e8d7c6b5a", + name: "Checkout errors", + notes: null, + notificationTemplate: null, + enabled: true, + severity: "critical", + serviceNames: ["checkout"], + excludeServiceNames: [], + environments: [], + tags: [], + groupBy: null, + signalType: "error_rate", + comparator: "gt", + threshold: 2, + thresholdUpper: null, + windowMinutes: 5, + minimumSampleCount: 0, + consecutiveBreachesRequired: 2, + breachesOutOf: null, + evaluationIntervalMinutes: null, + thresholds: tiers, + consecutiveHealthyRequired: 2, + renotifyIntervalMinutes: 30, + apdexThresholdMs: null, + queryBuilderDraft: null, + rawQuerySql: null, + rawQueryReducer: null, + destinationIds: [], + noDataBehavior: "skip", + lastEvaluationError: null, + lastEvaluationErrorKind: null, + slowedIntervalMinutes: null, + slowedAt: null, + lastEvaluatedAt: iso, + lastScheduledAt: iso, + createdAt: iso, + updatedAt: iso, + createdBy: "user_test", + updatedBy: "user_test", + }) + const [warning, critical] = formThresholds(ruleToFormState(rule)) + expect(warning?.escalation_chain_id).toBe(warnChain) + expect(critical?.escalation_chain_id).toBe(pageChain) + }) + it("saves each tier's chain on that tier", () => { const [warning, critical] = formThresholds(twoTiers) expect(warning).toMatchObject({ severity: "warning", escalation_chain_id: warnChain }) From 46dd4dad2e65f947f708b081b5073f121a6bf18f Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 14:14:26 +0100 Subject: [PATCH 17/29] fix(lint): effect/Array.findFirst for the delivery log's connector lookup --- apps/web/src/page/alerts/hub/view-routing.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/page/alerts/hub/view-routing.ts b/apps/web/src/page/alerts/hub/view-routing.ts index 5c2fd2f020..713f591000 100644 --- a/apps/web/src/page/alerts/hub/view-routing.ts +++ b/apps/web/src/page/alerts/hub/view-routing.ts @@ -456,7 +456,9 @@ const logRow = (h: H, model: Model, inputs: RoutingInputs, event: AlertDeliveryE const ev = eventTypeMeta[event.eventType] const status = deliveryStatusMeta[event.status] // Delivery events carry only the type; a `chat` destination's logo comes from its connector. - const chatConnector = inputs.destinations.find((d) => d.id === event.destinationId)?.chatConnector + const chatConnector = Option.getOrUndefined( + Arr.findFirst(inputs.destinations, (d) => d.id === event.destinationId), + )?.chatConnector return tableRow(h, [ tableCell(h, [ hstack(h, { gap: "1.5", as: "span" }, [ From b8defc3f41de031a13283f8068979b01f2ce545b Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 14:25:08 +0100 Subject: [PATCH 18/29] chore(mcp): regenerate tool descriptors after merging main --- apps/ai/src/mcp/tool-descriptors.generated.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 1da2b6d3b6..6004aa6db4 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -27,17 +27,21 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"query_data","title":"Query Data","description":"Aggregate traces, logs, metrics or product events into a timeseries or a top-N breakdown. Start here for trends, comparisons and top-N; for errors prefer find_errors and error_detail; for a shape this tool cannot express, run_sql. Applied defaults are listed in the result. A traces breakdown by span_name or attribute reads raw spans: set `service` and keep the window short (a window too large is narrowed, with a note). For endpoint volume over days use route_usage.","inputSchema":{"type":"object","properties":{"source":{"type":"string","enum":["traces","logs","metrics","product_events"],"description":"'traces' for request/span analysis (latency, errors, throughput); 'logs' for log volume; 'metrics' for a custom metric (needs metric_name and metric_type from list_metrics); 'product_events' for track() events, page views and server events (names from list_product_events)."},"kind":{"type":"string","enum":["timeseries","breakdown"],"description":"'timeseries' for how a value changed over time; 'breakdown' for top-N or distribution. Pick the right one first rather than calling twice."},"metric":{"type":"string","description":"Traces: count, avg_duration, p50_duration, p95_duration, p99_duration, error_rate (0-1 ratio), apdex (needs apdex_threshold_ms). Logs: count. Product events: count, sessions, persons, users, visitors. Metrics: avg, sum, min, max, count, rate, increase. For counters (sum metrics, Prometheus *_total gauges) use increase (total over the window) or rate (per second); both are per-series and reset-aware. sum adds raw samples. Default: count, or avg for metrics."},"group_by":{"type":"string","description":"Traces: service, span_name, status_code, http_method, attribute, resource_attribute. Logs: service, severity. Metrics: service, attribute, resource_attribute. Product events: event_name, kind, source, host, page_path, service, group, attribute. 'attribute' and 'resource_attribute' need attribute_key (resource_attribute reads resource keys such as k8s.pod.name or deployment.environment). Timeseries also take 'none' (the default); breakdown defaults to service."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 6 hours before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"service":{"type":"string","description":"Only this service (exact `service.name`)"},"span_name":{"type":"string","description":"Filter by span name (traces only). Exact match first; when nothing matches exactly it is matched as a case-insensitive substring"},"root_spans_only":{"type":"boolean","description":"Only include root spans (traces only)"},"environments":{"type":"array","items":{"type":"string"},"description":"Only these deployment environments (traces, logs, metrics; explore_attributes source=services lists them)"},"commit_shas":{"type":"array","items":{"type":"string"},"description":"Commit SHAs to filter (traces only)"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in ms (traces only; needed for metric=apdex)"},"severity":{"type":"string","enum":["TRACE","DEBUG","INFO","WARN","ERROR","FATAL"],"description":"Only this log severity (logs only)"},"event_name":{"type":"array","items":{"type":"string"},"description":"Only these event names (product_events only)"},"event_kind":{"type":"array","items":{"type":"string","enum":["navigation","custom","screen"]},"description":"Only these event kinds (product_events only)"},"host":{"type":"string","description":"Only events fired on this site host (product_events only)"},"page_path":{"type":"string","description":"Only events fired on this page path (product_events only)"},"metric_name":{"type":"string","description":"The metric, from list_metrics (source=metrics only)"},"metric_type":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"],"description":"Type of `metric_name`, as list_metrics reports it"},"attribute_key":{"type":"string","description":"Attribute to filter on, or to group by with group_by=attribute. Span (or log/metric label) and resource attributes both work; the scope is resolved automatically"},"attribute_value":{"type":"string","description":"Exact value for attribute_key. Also applies when grouping by the same key"},"attribute_scope":{"type":"string","enum":["span","resource"],"description":"Where attribute_key lives: 'span' (span, log or metric-datapoint attributes) or 'resource' (deployment.environment, k8s.pod.name...). Resolved from the known keys when omitted"},"bucket_seconds":{"type":"number","description":"Bucket width in seconds (timeseries only; auto-computed when omitted)"},"limit":{"type":"number","description":"Max rows of a breakdown (default 10, max 100)"}},"required":["source","kind"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"kind":{"type":"string"},"metric":{"type":"string"},"groupBy":{"type":"string"},"decisions":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}},"queryContext":{"type":"object","properties":{"source":{"type":"string","enum":["traces","logs","metrics","product_events"]},"serviceName":{"type":"string"},"eventName":{"type":"string"},"eventKind":{"type":"string"},"host":{"type":"string"},"pagePath":{"type":"string"},"spanName":{"type":"string"},"rootSpansOnly":{"type":"boolean"},"environments":{"type":"array","items":{"type":"string"}},"commitShas":{"type":"array","items":{"type":"string"}},"severity":{"type":"string"},"metricName":{"type":"string"},"metricType":{"type":"string"},"attributeFilters":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"},"mode":{"type":"string"}},"required":["key","mode"],"additionalProperties":true}},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"bucketSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["source"],"additionalProperties":true},"unit":{"type":"string","enum":["duration_ms","duration_us","duration_s","duration_ns","percent","number","bytes","requests_per_sec"]},"result":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["timeseries"]},"data":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string"},"series":{"type":"object","additionalProperties":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["bucket","series"],"additionalProperties":true}}},"required":["kind","data"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["breakdown"]},"data":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["name","value"],"additionalProperties":true}}},"required":["kind","data"],"additionalProperties":true}]},"emptyHints":{"type":"array","items":{"type":"string"}}},"required":["timeRange","kind","metric","queryContext","unit","result"],"additionalProperties":true},"annotations":{"title":"Query Data","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"run_sql","title":"Run SQL","description":"Run one read-only ClickHouse SELECT against the org's warehouse and return its rows: at most 1000 are fetched and the first 100 rendered, with the count reported. Use it for what query_data cannot express, to spot-check data, or to test SQL for a raw_sql widget. For trends and top-N prefer query_data. The SQL must AND in `$__orgFilter`; macros are listed on `sql`. Spans are in `traces` (no otel_spans/spans table), log lines in `logs`; also metrics_sum/metrics_gauge/metrics_histogram, error_events, session_replays, product_events and *_hourly/*_minutely rollups. Select 64-bit IDs and hashes as toString(col). Attribute columns are Map(String, String): SpanAttributes['http.route'], never JSON* functions. In traces the name is SpanName, StatusCode is 'Ok'/'Error' and Duration is nanoseconds. Compare timestamps to 'YYYY-MM-DD HH:MM:SS', not ISO T/Z strings; bucket with toStartOfInterval. Call describe_warehouse_tables for a table's columns, units and enum casing before guessing names.","inputSchema":{"type":"object","properties":{"sql":{"type":"string","description":"The SELECT. It MUST contain `$__orgFilter` (expands to `(OrgId = '')`) as a top-level AND condition; a query without it, or with it OR-ed against another condition, is rejected. In joins use `$__orgFilter(alias)` (`(alias.OrgId = '')`) once per joined table. Other macros: `$__timeFilter(Column)` (`Column >= AND Column <= `), `$__startTime`, `$__endTime`, `$__interval_s` (bucket width for toStartOfInterval). Only one statement; DDL/DML, SETTINGS and system tables are rejected."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 6 hours before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"granularity_seconds":{"type":"number","description":"Value of `$__interval_s`. Auto-computed from the window when omitted."},"max_cell_chars":{"type":"number","description":"Clip each rendered cell to this many characters (default 120, max 2000). Raise it to read long values such as log bodies or stack traces."},"show_sql":{"type":"boolean","description":"Also return the macro-expanded SQL that ran (default false)."},"reason":{"type":"string","description":"Why you are running this query, in a sentence. Optional; does not change the query."}},"required":["sql"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rowCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"rows":{"type":"array","items":{"type":"object"}},"truncated":{"type":"boolean"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"maxCellChars":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowApplied":{"type":"boolean"},"expandedSql":{"type":"string"},"atRowCap":{"type":"boolean"},"rewrites":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"additionalProperties":true}}},"required":["rowCount","columns","rows","truncated","timeRange"],"additionalProperties":true},"annotations":{"title":"Run SQL","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"service_map","title":"Service Map","description":"Show service-to-service dependencies with call counts, error rates, and latency per edge. Use to understand system architecture and identify problematic inter-service calls.","inputSchema":{"type":"object","properties":{"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 6 hours before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"service":{"type":"string","description":"Only edges involving this service, as source or target (exact `service.name`)"},"environment":{"type":"string","description":"Only this deployment environment (e.g. production, staging)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"edges":{"type":"array","items":{"type":"object","properties":{"sourceService":{"type":"string"},"targetService":{"type":"string"},"callCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errorCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"avgDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"maxDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["sourceService","targetService","callCount","errorCount","avgDurationMs","maxDurationMs"],"additionalProperties":true}},"serviceCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"service":{"type":"string"},"environment":{"type":"string"}},"required":["timeRange","edges","serviceCount"],"additionalProperties":true},"annotations":{"title":"Service Map","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"list_alert_rules","title":"List Alert Rules","description":"List configured alert rules with severity, signal type and condition. Use list_alert_incidents for what has fired.","inputSchema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"string"},"description":"Only rules covering any of these services: scoped to them, scoped to every service, or raw_query rules whose SQL or name mentions them"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"Only rules on this signal type"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only rules with this severity"},"enabled_only":{"type":"boolean","description":"Only enabled rules"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"services":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"severity":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["rules","total"],"additionalProperties":true},"annotations":{"title":"List Alert Rules","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"list_alert_rules","title":"List Alert Rules","description":"List configured alert rules with severity, signal type and condition. Use list_alert_incidents for what has fired.","inputSchema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"string"},"description":"Only rules covering any of these services: scoped to them, scoped to every service, or raw_query rules whose SQL or name mentions them"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"Only rules on this signal type"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only rules with this severity"},"enabled_only":{"type":"boolean","description":"Only enabled rules"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"services":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"severity":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["rules","total"],"additionalProperties":true},"annotations":{"title":"List Alert Rules","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_destinations","title":"List Alert Destinations","description":"List the notification destinations a rule can deliver to, with id, type and delivery health. Pass the ids as destination_ids to create_alert_rule / update_alert_rule.","inputSchema":{"type":"object","properties":{"type":{"type":"string","enum":["pagerduty","webhook","discord","telegram","google-chat","microsoft-teams","incident-io","email","chat","linear"],"description":"Only destinations of this type"},"enabled_only":{"type":"boolean","description":"Only enabled destinations"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"enabled":{"type":"boolean"},"summary":{"type":"string"},"channelLabel":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestError":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabledReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","type","enabled","summary","channelLabel","lastTestedAt","lastTestError","disabledReason","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"type":{"type":"string"},"enabledOnly":{"type":"boolean"}},"required":["destinations","total"],"additionalProperties":true},"annotations":{"title":"List Alert Destinations","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"get_alert_rule","title":"Get Alert Rule","description":"Get full configuration details of a specific alert rule including thresholds, service filters, evaluation settings, and notification destinations. Use list_alert_rules to find rule IDs.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"excludeServiceNames":{"type":"array","items":{"type":"string"}},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noDataBehavior":{"type":"string"},"consecutiveBreachesRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthyRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"renotifyIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"queryBuilderDraft":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"null"}]},"rawQuerySql":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawQueryReducer":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"notificationTitle":{"anyOf":[{"type":"string"},{"type":"null"}]},"notificationBody":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationError":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationErrorKind":{"anyOf":[{"type":"string"},{"type":"null"}]},"slowedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}},"required":["id","name","type","enabled"],"additionalProperties":true}}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt","excludeServiceNames","groupBy","minimumSampleCount","noDataBehavior","consecutiveBreachesRequired","consecutiveHealthyRequired","renotifyIntervalMinutes","apdexThresholdMs","queryBuilderDraft","rawQuerySql","rawQueryReducer"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Get Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"get_alert_rule","title":"Get Alert Rule","description":"Get full configuration details of a specific alert rule including thresholds, service filters, evaluation settings, and notification destinations. Use list_alert_rules to find rule IDs.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"excludeServiceNames":{"type":"array","items":{"type":"string"}},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noDataBehavior":{"type":"string"},"consecutiveBreachesRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthyRequired":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"renotifyIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"apdexThresholdMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"queryBuilderDraft":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"null"}]},"rawQuerySql":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawQueryReducer":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"notificationTitle":{"anyOf":[{"type":"string"},{"type":"null"}]},"notificationBody":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationError":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluationErrorKind":{"anyOf":[{"type":"string"},{"type":"null"}]},"slowedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastEvaluatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"destinations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]},"enabled":{"type":"boolean"}},"required":["id","name","type","enabled"],"additionalProperties":true}}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt","excludeServiceNames","groupBy","minimumSampleCount","noDataBehavior","consecutiveBreachesRequired","consecutiveHealthyRequired","renotifyIntervalMinutes","apdexThresholdMs","queryBuilderDraft","rawQuerySql","rawQueryReducer"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Get Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_incidents","title":"List Alert Incidents","description":"Alert incidents: alert-rule breaches, open and resolved, with severity, group, condition and last observed value (flare-ups of an error issue are list_error_incidents). For one rule's trigger, notify and resolve timestamps use get_incident_timeline.","inputSchema":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"acknowledged":{"type":"boolean","description":"true: open incidents someone acknowledged; false: open incidents nobody has. Implies status=open"},"limit":{"type":"number","description":"Max incidents to return (default 50, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"holdReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"heldSince":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"firstTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","holdReason","heldSince","threshold","comparator","firstTriggeredAt","resolvedAt","lastObservedValue"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"recentlyResolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"List Alert Incidents","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_alert_checks","title":"List Alert Checks","description":"List a rule's recent checks (one row per evaluation) with observed value, threshold, sample count and incident transition. Use it to tune thresholds, diagnose flapping, or see the near-misses before a breach.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"group_key":{"type":"string","description":"Only checks for this exact group key"},"status":{"type":"string","enum":["breached","healthy","skipped","error"],"description":"Only checks with this status"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."},"limit":{"type":"number","description":"Max checks to return (default 100, max 2000)"}},"required":["rule_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"skipped":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errored":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transitions":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"checks":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"string"},"groupKey":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"observedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowStart":{"type":"string"},"windowEnd":{"type":"string"},"consecutiveBreaches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"consecutiveHealthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"incidentId":{"anyOf":[{"type":"string"},{"type":"null"}]},"incidentTransition":{"type":"string"},"evaluationDurationMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"errorMessage":{"anyOf":[{"type":"string"},{"type":"null"}]},"errorCategory":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["timestamp","groupKey","status","skipReason","observedValue","threshold","comparator","sampleCount","windowStart","windowEnd","consecutiveBreaches","consecutiveHealthy","incidentId","incidentTransition","evaluationDurationMs","errorMessage","errorCategory"],"additionalProperties":true}},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"groupKey":{"type":"string"},"status":{"type":"string"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["ruleId","total","breached","healthy","skipped","noData","belowMinSamples","errored","transitions","checks"],"additionalProperties":true},"annotations":{"title":"List Alert Checks","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations).","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, - {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations). escalation_chain_id adds step-by-step paging on top.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, + {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or \"none\" to detach it from every tier. On a rule with warning and critical tiers an id sets the critical tier: its UUID or its REST id (esc_\u2026)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_alert_rule","title":"Delete Alert Rule","description":"Permanently delete an alert rule together with its incident history, delivery events and evaluation state. Irreversible; requires confirm=true. Ids from list_alert_rules.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"confirm":{"type":"boolean","description":"Must be true. There is no undo."}},"required":["rule_id","confirm"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"annotations":{"title":"Delete Alert Rule","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleId","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, + {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id=\"none\" first. Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_dashboard","title":"Get Dashboard","description":"Full configuration of one dashboard: every widget with its id, visualization, dataSource, display, layout and any pinned timeRange. Widgets come back in the shape update_dashboard_widget and replace_dashboard_widgets take. Ids from list_dashboards.","inputSchema":{"type":"object","properties":{"dashboard_id":{"type":"string","description":"Dashboard ID: its UUID or its REST id (dash_\u2026)"}},"required":["dashboard_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"publicId":{"type":"string"},"dashboard":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"widgets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"visualization":{"type":"string","enum":["chart","hbar","stat","gauge","table","list","pie","histogram","heatmap","funnel","paths","markdown"]},"dataSource":{"$ref":"#/$defs/@maple~1WidgetDataSource"},"display":{"type":"object","properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartPresentation":{"anyOf":[{"type":"object","properties":{"legend":{"anyOf":[{"type":"string","enum":["visible","hidden","right"]},{"type":"null"}]},"seriesStats":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tooltip":{"anyOf":[{"type":"string","enum":["visible","hidden"]},{"type":"null"}]},"showPoints":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fillNulls":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"boolean","enum":[false]},{"type":"null"}]},"compareToPreviousPeriod":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"xAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"yAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMin":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMax":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScale":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fitYAxisToData":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"seriesMapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"colorOverrides":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"stacked":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"curveType":{"anyOf":[{"type":"string","enum":["linear","monotone"]},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}]},"sparkline":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"dataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1WidgetDataSource"},{"type":"null"}]}},"required":["enabled"],"additionalProperties":true},{"type":"null"}]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"header":{"type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"align":{"anyOf":[{"type":"string","enum":["left","center","right"]},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]}},"required":["field","header"],"additionalProperties":true}},{"type":"null"}]},"listDataSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"listWhereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"listLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"listRootOnly":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"pie":{"anyOf":[{"type":"object","properties":{"donut":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"innerRadius":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"showLabels":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"showPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"funnel":{"anyOf":[{"type":"object","properties":{"showStepPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["session"]},"dimension":{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},"value":{"type":"string"}},"required":["kind","dimension","value"],"additionalProperties":true}]}},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"breakdownBy":{"anyOf":[{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},{"type":"string","pattern":"^attribute:[\\s\\S]*?$"},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","enum":["bars","dropoff"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"paths":{"anyOf":[{"type":"object","properties":{"anchor":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true}]},"direction":{"anyOf":[{"type":"string","enum":["after","before"]},{"type":"null"}]},"depth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"branches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"include":{"anyOf":[{"type":"string","enum":["all","events","pages"]},{"type":"null"}]},"exclude":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]}},"required":["anchor"],"additionalProperties":true},{"type":"null"}]},"histogram":{"anyOf":[{"type":"object","properties":{"bucketCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"bucketWidth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScaleY":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"heatmap":{"anyOf":[{"type":"object","properties":{"colorScale":{"anyOf":[{"type":"string","enum":["amber","blues","reds","viridis","magma","cividis"]},{"type":"null"}]},"scaleType":{"anyOf":[{"type":"string","enum":["linear","log"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"gauge":{"anyOf":[{"type":"object","properties":{"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"style":{"anyOf":[{"type":"string","enum":["radial","bar"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"markdown":{"anyOf":[{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"layout":{"type":"object","properties":{"x":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"y":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"w":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"h":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["x","y","w","h"],"additionalProperties":true},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"sectionId":{"type":"string"},"tabId":{"type":"string"}},"required":["id","visualization","dataSource","display","layout"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","tags","timeRange","widgets","createdAt","updatedAt"],"additionalProperties":true}},"required":["publicId","dashboard"],"additionalProperties":true,"$defs":{"@maple/IsoDateTimeString":{"type":"string"},"@maple/QueryWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["query"]},"resultShape":{"type":"string","enum":["timeseries","breakdown","list"]},"queries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true}]}},"formulas":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expression":{"type":"string"},"legend":{"type":"string"},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","expression","legend"],"additionalProperties":true}},{"type":"null"}]},"comparison":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"type":"string","enum":["none","previous_period"]},{"type":"null"}]},"includePercentChange":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"defaultLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","resultShape","queries"],"additionalProperties":true,"title":"Query widget data source"},"@maple/RawSqlWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["raw_sql"]},"sql":{"type":"string"},"displayType":{"type":"string"},"granularitySeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","sql"],"additionalProperties":true,"title":"Raw SQL widget data source"},"@maple/RouteWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["route"]},"endpoint":{"type":"string"},"params":{"type":"object"},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","endpoint"],"additionalProperties":true,"title":"Route widget data source"},"@maple/StaticWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["static"]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind"],"additionalProperties":true,"title":"Static widget data source"},"@maple/WidgetDataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1QueryWidgetDataSource"},{"$ref":"#/$defs/@maple~1RawSqlWidgetDataSource"},{"$ref":"#/$defs/@maple~1RouteWidgetDataSource"},{"$ref":"#/$defs/@maple~1StaticWidgetDataSource"}],"title":"Widget data source"}}},"annotations":{"title":"Get Dashboard","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, From 0a6a76b18940d0c270d9d686e995bbf1e357ea86 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 14:39:04 +0100 Subject: [PATCH 19/29] fix(alerts): an escalation step re-reads its incident before sending; chain detach guidance covers chain-only tiers --- apps/ai/src/mcp/lib/escalation-chains.ts | 2 +- .../src/services/alerts/AlertsService.ts | 23 +++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/ai/src/mcp/lib/escalation-chains.ts b/apps/ai/src/mcp/lib/escalation-chains.ts index 4bbf440b24..ccb1e99e7e 100644 --- a/apps/ai/src/mcp/lib/escalation-chains.ts +++ b/apps/ai/src/mcp/lib/escalation-chains.ts @@ -165,7 +165,7 @@ export const deleteChainErrors = { message: `Escalation chain is still used by ${Arr.join( Arr.map(error.ruleIds, (id, index) => `"${error.ruleNames[index] ?? id}" (${id})`), ", ", - )}. Detach it first with update_alert_rule escalation_chain_id="none".`, + )}. Detach it first with update_alert_rule escalation_chain_id="none"; a tier that pages only through the chain needs a destination first (destination_ids sets the first tier; a critical tier needs its own update).`, parameter: "chain_id", }), ), diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index bfa2fdc570..7c6906f3da 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -2459,16 +2459,29 @@ export class AlertsService extends Context.Service + db.run( + PG.from(AlertIncidents) + .select("status", "acknowledgedAt") + .where(($) => [$.orgId.eq(row.orgId), $.id.eq(stepIncidentId)]) + .limit(1), + ), + ))[0] ?? null) + : null + if ( + stepIncident !== null && + (stepIncident.status !== "open" || stepIncident.acknowledgedAt !== null) ) { yield* recordDeliveryFailure(row, claimTime, { message: - incidentRow.status !== "open" + stepIncident.status !== "open" ? "Not sent: the incident resolved before this escalation step went out" : "Not sent: the incident was acknowledged before this escalation step went out", kind: "escalation", From ab96cb071e54cc5d28c3d7ff7faf566f79ca0779 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 14:44:01 +0100 Subject: [PATCH 20/29] fix(mcp): delete_escalation_chain's description says a chain-only tier needs a destination before detaching --- apps/ai/src/mcp/tool-descriptors.generated.ts | 2 +- apps/ai/src/mcp/tools/delete-escalation-chain.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 6004aa6db4..5db102700d 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -41,7 +41,7 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id=\"none\" first. Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, + {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id=\"none\" first; a tier that pages only through the chain needs a destination before that. Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_dashboard","title":"Get Dashboard","description":"Full configuration of one dashboard: every widget with its id, visualization, dataSource, display, layout and any pinned timeRange. Widgets come back in the shape update_dashboard_widget and replace_dashboard_widgets take. Ids from list_dashboards.","inputSchema":{"type":"object","properties":{"dashboard_id":{"type":"string","description":"Dashboard ID: its UUID or its REST id (dash_\u2026)"}},"required":["dashboard_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"publicId":{"type":"string"},"dashboard":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"widgets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"visualization":{"type":"string","enum":["chart","hbar","stat","gauge","table","list","pie","histogram","heatmap","funnel","paths","markdown"]},"dataSource":{"$ref":"#/$defs/@maple~1WidgetDataSource"},"display":{"type":"object","properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartPresentation":{"anyOf":[{"type":"object","properties":{"legend":{"anyOf":[{"type":"string","enum":["visible","hidden","right"]},{"type":"null"}]},"seriesStats":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tooltip":{"anyOf":[{"type":"string","enum":["visible","hidden"]},{"type":"null"}]},"showPoints":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fillNulls":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"boolean","enum":[false]},{"type":"null"}]},"compareToPreviousPeriod":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"xAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"yAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMin":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMax":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScale":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fitYAxisToData":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"seriesMapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"colorOverrides":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"stacked":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"curveType":{"anyOf":[{"type":"string","enum":["linear","monotone"]},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}]},"sparkline":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"dataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1WidgetDataSource"},{"type":"null"}]}},"required":["enabled"],"additionalProperties":true},{"type":"null"}]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"header":{"type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"align":{"anyOf":[{"type":"string","enum":["left","center","right"]},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]}},"required":["field","header"],"additionalProperties":true}},{"type":"null"}]},"listDataSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"listWhereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"listLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"listRootOnly":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"pie":{"anyOf":[{"type":"object","properties":{"donut":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"innerRadius":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"showLabels":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"showPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"funnel":{"anyOf":[{"type":"object","properties":{"showStepPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["session"]},"dimension":{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},"value":{"type":"string"}},"required":["kind","dimension","value"],"additionalProperties":true}]}},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"breakdownBy":{"anyOf":[{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},{"type":"string","pattern":"^attribute:[\\s\\S]*?$"},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","enum":["bars","dropoff"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"paths":{"anyOf":[{"type":"object","properties":{"anchor":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true}]},"direction":{"anyOf":[{"type":"string","enum":["after","before"]},{"type":"null"}]},"depth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"branches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"include":{"anyOf":[{"type":"string","enum":["all","events","pages"]},{"type":"null"}]},"exclude":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]}},"required":["anchor"],"additionalProperties":true},{"type":"null"}]},"histogram":{"anyOf":[{"type":"object","properties":{"bucketCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"bucketWidth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScaleY":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"heatmap":{"anyOf":[{"type":"object","properties":{"colorScale":{"anyOf":[{"type":"string","enum":["amber","blues","reds","viridis","magma","cividis"]},{"type":"null"}]},"scaleType":{"anyOf":[{"type":"string","enum":["linear","log"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"gauge":{"anyOf":[{"type":"object","properties":{"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"style":{"anyOf":[{"type":"string","enum":["radial","bar"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"markdown":{"anyOf":[{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"layout":{"type":"object","properties":{"x":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"y":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"w":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"h":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["x","y","w","h"],"additionalProperties":true},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"sectionId":{"type":"string"},"tabId":{"type":"string"}},"required":["id","visualization","dataSource","display","layout"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","tags","timeRange","widgets","createdAt","updatedAt"],"additionalProperties":true}},"required":["publicId","dashboard"],"additionalProperties":true,"$defs":{"@maple/IsoDateTimeString":{"type":"string"},"@maple/QueryWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["query"]},"resultShape":{"type":"string","enum":["timeseries","breakdown","list"]},"queries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true}]}},"formulas":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expression":{"type":"string"},"legend":{"type":"string"},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","expression","legend"],"additionalProperties":true}},{"type":"null"}]},"comparison":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"type":"string","enum":["none","previous_period"]},{"type":"null"}]},"includePercentChange":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"defaultLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","resultShape","queries"],"additionalProperties":true,"title":"Query widget data source"},"@maple/RawSqlWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["raw_sql"]},"sql":{"type":"string"},"displayType":{"type":"string"},"granularitySeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","sql"],"additionalProperties":true,"title":"Raw SQL widget data source"},"@maple/RouteWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["route"]},"endpoint":{"type":"string"},"params":{"type":"object"},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","endpoint"],"additionalProperties":true,"title":"Route widget data source"},"@maple/StaticWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["static"]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind"],"additionalProperties":true,"title":"Static widget data source"},"@maple/WidgetDataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1QueryWidgetDataSource"},{"$ref":"#/$defs/@maple~1RawSqlWidgetDataSource"},{"$ref":"#/$defs/@maple~1RouteWidgetDataSource"},{"$ref":"#/$defs/@maple~1StaticWidgetDataSource"}],"title":"Widget data source"}}},"annotations":{"title":"Get Dashboard","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/delete-escalation-chain.ts b/apps/ai/src/mcp/tools/delete-escalation-chain.ts index 73dab98df8..518ce73bab 100644 --- a/apps/ai/src/mcp/tools/delete-escalation-chain.ts +++ b/apps/ai/src/mcp/tools/delete-escalation-chain.ts @@ -12,7 +12,7 @@ export function registerDeleteEscalationChainTool(server: McpToolRegistrar) { name: "delete_escalation_chain", title: "Delete Escalation Chain", description: - 'Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id="none" first. ' + + 'Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id="none" first; a tier that pages only through the chain needs a destination before that. ' + "Escalations already running finish. Ids from list_escalation_chains.", parameters: Schema.Struct({ chain_id: P.resourceId("escalationChain", "Escalation chain to delete"), From 81bd621ecc2d8ea654403cd98547c449323dd220 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 14:44:42 +0100 Subject: [PATCH 21/29] fix(alerts): escalation spans record the org as orgId; the detach hint names how a critical tier gains a destination --- apps/ai/src/mcp/lib/escalation-chains.ts | 2 +- apps/ai/src/mcp/tool-descriptors.generated.ts | 2 +- apps/ai/src/mcp/tools/delete-escalation-chain.ts | 2 +- packages/backend/src/services/alerts/AlertsService.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/ai/src/mcp/lib/escalation-chains.ts b/apps/ai/src/mcp/lib/escalation-chains.ts index ccb1e99e7e..6e5ab5157a 100644 --- a/apps/ai/src/mcp/lib/escalation-chains.ts +++ b/apps/ai/src/mcp/lib/escalation-chains.ts @@ -165,7 +165,7 @@ export const deleteChainErrors = { message: `Escalation chain is still used by ${Arr.join( Arr.map(error.ruleIds, (id, index) => `"${error.ruleNames[index] ?? id}" (${id})`), ", ", - )}. Detach it first with update_alert_rule escalation_chain_id="none"; a tier that pages only through the chain needs a destination first (destination_ids sets the first tier; a critical tier needs its own update).`, + )}. Detach it first with update_alert_rule escalation_chain_id="none"; a tier that pages only through the chain needs a destination first (destination_ids does that on a one-tier rule; a two-tier rule's critical tier gains one by replacing the rule's thresholds in the v2 API or the dashboard).`, parameter: "chain_id", }), ), diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 5db102700d..2b66db4741 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -41,7 +41,7 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id=\"none\" first; a tier that pages only through the chain needs a destination before that. Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, + {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id=\"none\" first; a tier that pages only through the chain needs a destination before that (destination_ids on a one-tier rule; the rule's thresholds via the v2 API or dashboard on a two-tier rule). Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_dashboard","title":"Get Dashboard","description":"Full configuration of one dashboard: every widget with its id, visualization, dataSource, display, layout and any pinned timeRange. Widgets come back in the shape update_dashboard_widget and replace_dashboard_widgets take. Ids from list_dashboards.","inputSchema":{"type":"object","properties":{"dashboard_id":{"type":"string","description":"Dashboard ID: its UUID or its REST id (dash_\u2026)"}},"required":["dashboard_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"publicId":{"type":"string"},"dashboard":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"widgets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"visualization":{"type":"string","enum":["chart","hbar","stat","gauge","table","list","pie","histogram","heatmap","funnel","paths","markdown"]},"dataSource":{"$ref":"#/$defs/@maple~1WidgetDataSource"},"display":{"type":"object","properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartPresentation":{"anyOf":[{"type":"object","properties":{"legend":{"anyOf":[{"type":"string","enum":["visible","hidden","right"]},{"type":"null"}]},"seriesStats":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tooltip":{"anyOf":[{"type":"string","enum":["visible","hidden"]},{"type":"null"}]},"showPoints":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fillNulls":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"boolean","enum":[false]},{"type":"null"}]},"compareToPreviousPeriod":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"xAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"yAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMin":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMax":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScale":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fitYAxisToData":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"seriesMapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"colorOverrides":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"stacked":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"curveType":{"anyOf":[{"type":"string","enum":["linear","monotone"]},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}]},"sparkline":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"dataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1WidgetDataSource"},{"type":"null"}]}},"required":["enabled"],"additionalProperties":true},{"type":"null"}]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"header":{"type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"align":{"anyOf":[{"type":"string","enum":["left","center","right"]},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]}},"required":["field","header"],"additionalProperties":true}},{"type":"null"}]},"listDataSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"listWhereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"listLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"listRootOnly":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"pie":{"anyOf":[{"type":"object","properties":{"donut":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"innerRadius":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"showLabels":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"showPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"funnel":{"anyOf":[{"type":"object","properties":{"showStepPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["session"]},"dimension":{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},"value":{"type":"string"}},"required":["kind","dimension","value"],"additionalProperties":true}]}},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"breakdownBy":{"anyOf":[{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},{"type":"string","pattern":"^attribute:[\\s\\S]*?$"},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","enum":["bars","dropoff"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"paths":{"anyOf":[{"type":"object","properties":{"anchor":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true}]},"direction":{"anyOf":[{"type":"string","enum":["after","before"]},{"type":"null"}]},"depth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"branches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"include":{"anyOf":[{"type":"string","enum":["all","events","pages"]},{"type":"null"}]},"exclude":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]}},"required":["anchor"],"additionalProperties":true},{"type":"null"}]},"histogram":{"anyOf":[{"type":"object","properties":{"bucketCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"bucketWidth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScaleY":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"heatmap":{"anyOf":[{"type":"object","properties":{"colorScale":{"anyOf":[{"type":"string","enum":["amber","blues","reds","viridis","magma","cividis"]},{"type":"null"}]},"scaleType":{"anyOf":[{"type":"string","enum":["linear","log"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"gauge":{"anyOf":[{"type":"object","properties":{"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"style":{"anyOf":[{"type":"string","enum":["radial","bar"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"markdown":{"anyOf":[{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"layout":{"type":"object","properties":{"x":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"y":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"w":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"h":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["x","y","w","h"],"additionalProperties":true},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"sectionId":{"type":"string"},"tabId":{"type":"string"}},"required":["id","visualization","dataSource","display","layout"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","tags","timeRange","widgets","createdAt","updatedAt"],"additionalProperties":true}},"required":["publicId","dashboard"],"additionalProperties":true,"$defs":{"@maple/IsoDateTimeString":{"type":"string"},"@maple/QueryWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["query"]},"resultShape":{"type":"string","enum":["timeseries","breakdown","list"]},"queries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true}]}},"formulas":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expression":{"type":"string"},"legend":{"type":"string"},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","expression","legend"],"additionalProperties":true}},{"type":"null"}]},"comparison":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"type":"string","enum":["none","previous_period"]},{"type":"null"}]},"includePercentChange":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"defaultLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","resultShape","queries"],"additionalProperties":true,"title":"Query widget data source"},"@maple/RawSqlWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["raw_sql"]},"sql":{"type":"string"},"displayType":{"type":"string"},"granularitySeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","sql"],"additionalProperties":true,"title":"Raw SQL widget data source"},"@maple/RouteWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["route"]},"endpoint":{"type":"string"},"params":{"type":"object"},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","endpoint"],"additionalProperties":true,"title":"Route widget data source"},"@maple/StaticWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["static"]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind"],"additionalProperties":true,"title":"Static widget data source"},"@maple/WidgetDataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1QueryWidgetDataSource"},{"$ref":"#/$defs/@maple~1RawSqlWidgetDataSource"},{"$ref":"#/$defs/@maple~1RouteWidgetDataSource"},{"$ref":"#/$defs/@maple~1StaticWidgetDataSource"}],"title":"Widget data source"}}},"annotations":{"title":"Get Dashboard","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/delete-escalation-chain.ts b/apps/ai/src/mcp/tools/delete-escalation-chain.ts index 518ce73bab..4fa933cd06 100644 --- a/apps/ai/src/mcp/tools/delete-escalation-chain.ts +++ b/apps/ai/src/mcp/tools/delete-escalation-chain.ts @@ -12,7 +12,7 @@ export function registerDeleteEscalationChainTool(server: McpToolRegistrar) { name: "delete_escalation_chain", title: "Delete Escalation Chain", description: - 'Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id="none" first; a tier that pages only through the chain needs a destination before that. ' + + 'Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id="none" first; a tier that pages only through the chain needs a destination before that (destination_ids on a one-tier rule; the rule\'s thresholds via the v2 API or dashboard on a two-tier rule). ' + "Escalations already running finish. Ids from list_escalation_chains.", parameters: Schema.Struct({ chain_id: P.resourceId("escalationChain", "Escalation chain to delete"), diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 7c6906f3da..58e0f284dc 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -5524,7 +5524,7 @@ export class AlertsService extends Context.Service Date: Fri, 6 Nov 2026 14:49:32 +0100 Subject: [PATCH 22/29] fix(alerts): stale escalation steps are dropped from chat digests too; update_alert_rule can set or detach one tier's chain --- apps/ai/src/mcp/lib/escalation-chains.ts | 2 +- apps/ai/src/mcp/tool-descriptors.generated.ts | 4 +- .../mcp/tools/__tests__/alert-tools.test.ts | 23 ++++ .../src/mcp/tools/delete-escalation-chain.ts | 2 +- apps/ai/src/mcp/tools/update-alert-rule.ts | 50 ++++++-- .../src/services/alerts/AlertsService.ts | 113 +++++++++++++----- 6 files changed, 149 insertions(+), 45 deletions(-) diff --git a/apps/ai/src/mcp/lib/escalation-chains.ts b/apps/ai/src/mcp/lib/escalation-chains.ts index 6e5ab5157a..bb4067e608 100644 --- a/apps/ai/src/mcp/lib/escalation-chains.ts +++ b/apps/ai/src/mcp/lib/escalation-chains.ts @@ -165,7 +165,7 @@ export const deleteChainErrors = { message: `Escalation chain is still used by ${Arr.join( Arr.map(error.ruleIds, (id, index) => `"${error.ruleNames[index] ?? id}" (${id})`), ", ", - )}. Detach it first with update_alert_rule escalation_chain_id="none"; a tier that pages only through the chain needs a destination first (destination_ids does that on a one-tier rule; a two-tier rule's critical tier gains one by replacing the rule's thresholds in the v2 API or the dashboard).`, + )}. Detach it first with update_alert_rule escalation_chain_id="none" and escalation_chain_tier set to the tier that names it, so another tier's chain stays. A tier that pages only through the chain needs a destination first: add_destination_ids adds to a one-tier rule or a two-tier rule's warning tier; a critical tier gains one by replacing the rule's thresholds in the v2 API or the dashboard.`, parameter: "chain_id", }), ), diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 2b66db4741..4424830019 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -35,13 +35,13 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"preview_alert_rule","title":"Preview Alert Rule","description":"Replay an alert rule over past data without saving it: per evaluation window, the value, sample count and verdict the scheduler would produce (breached, healthy, or skipped with the reason), plus when it would have fired. Pass rule_id to preview a saved rule, optionally with overrides, or a create_alert_rule definition to dry-run a new one. Run it before saving a raw_query rule.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Existing rule to preview (list_alert_rules). Definition params you also pass override its saved config, so a change can be tried before update_alert_rule. `template` is ignored with rule_id.: its UUID or its REST id (alrt_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"},"start_time":{"type":"string","description":"Window start, UTC (YYYY-MM-DD HH:mm:ss or ISO 8601). Default: 1 day before end_time. Covers at most 7 days; a wider window is narrowed to the 7 days before end_time."},"end_time":{"type":"string","description":"Window end, UTC, same format. Default: now."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"ruleId":{"type":"string"},"timeRange":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"],"additionalProperties":true},"truncatedToStart":{"anyOf":[{"type":"string"},{"type":"null"}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"thresholdUpper":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minimumSampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"groups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"windows":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"breached":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"healthy":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noData":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"belowMinSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"noValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"totalSamples":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","windows","breached","healthy","noData","belowMinSamples","noValue","minValue","maxValue","totalSamples"],"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"bucket":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"provisional":{"type":"boolean"}},"required":["groupKey","bucket","status","skipReason","value","sampleCount","provisional"],"additionalProperties":true}},"wouldFire":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"}},"required":["groupKey","start","end"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"seriesFailure":{"type":"string","enum":["query_too_slow","query_too_much_memory","not_time_grouped"]},"queryTiming":{"type":"object","properties":{"elapsedMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"failure":{"anyOf":[{"type":"string"},{"type":"null"}]},"probeLimitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"intervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limitMs":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"fitsIntervalMinutes":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}}},"required":["elapsedMs","failure","probeLimitMs","intervalMinutes","limitMs","fitsIntervalMinutes"],"additionalProperties":true},"performanceHints":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"line":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["code","message","line"],"additionalProperties":true}}},"required":["timeRange","truncatedToStart","windowMinutes","comparator","threshold","thresholdUpper","minimumSampleCount","groups","points","wouldFire","warnings"],"additionalProperties":true},"annotations":{"title":"Preview Alert Rule","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_incident_timeline","title":"Incident Timeline","description":"Alert-rule incidents (not error-issue incidents) for one rule or all, with first and last trigger, last notification and resolution timestamps: the per-incident detail behind list_alert_incidents. Pass rule_id to follow one rule over time.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Only incidents of this rule (ids from list_alert_rules): its UUID or its REST id (alrt_\u2026)"},"status":{"type":"string","enum":["open","resolved"],"description":"Only incidents in this status"},"severity":{"type":"string","enum":["warning","critical"],"description":"Only incidents with this severity"},"group_key":{"type":"string","description":"Only incidents with this exact group key"},"limit":{"type":"number","description":"Max incidents to return (default 20, max 200)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"incidents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"signalType":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"lastObservedValue":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"firstTriggeredAt":{"type":"string"},"lastTriggeredAt":{"type":"string"},"resolvedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastNotifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","ruleId","ruleName","groupKey","signalType","severity","status","comparator","threshold","lastObservedValue","firstTriggeredAt","lastTriggeredAt","resolvedAt","lastNotifiedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"openCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"resolvedCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleId":{"type":"string"},"status":{"type":"string"},"severity":{"type":"string"},"groupKey":{"type":"string"},"acknowledged":{"type":"boolean"},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"hasMore":{"type":"boolean"}},"required":["incidents","total","openCount","resolvedCount"],"additionalProperties":true},"annotations":{"title":"Incident Timeline","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_alert_rule","title":"Create Alert Rule","description":"Create an alert rule. Pick a `template` for the common cases; otherwise pass signal_type, comparator and threshold yourself. destination_ids is required (at least one, from list_alert_destinations). escalation_chain_id adds step-by-step paging on top.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Required, at least one: destination IDs to notify, from list_alert_destinations. Each is its UUID or its REST id (dest_\u2026)"},"template":{"type":"string","enum":["high_error_rate","slow_p95","slow_p99","low_apdex","throughput_drop","custom"],"description":"Preset for signal_type, comparator and threshold; signal_type and comparator you pass are ignored, threshold overrides. high_error_rate: error_rate > 0.05, grouped by service.name unless services is set. slow_p95: p95_latency > 1000 ms. slow_p99: p99_latency > 2000 ms. low_apdex: apdex < 0.8 with apdex_threshold_ms 500. throughput_drop: throughput < 100 rpm. Omit (or pass custom) to set the three yourself."},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity (default: warning)"},"threshold":{"type":"number","description":"The value compared against. Units: error_rate and apdex are 0-1 ratios, latency is milliseconds, throughput is requests per minute. Overrides the template's."},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440 (default: 5)"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit (or pass \"auto\") to derive it: a fifth of the window, 1 to 5."},"services":{"type":"array","items":{"type":"string"},"description":"Service names to scope the alert to"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (e.g. 'production'). Omit for all environments. Ignored for builder_query / raw_query, which filter inside their own query."},"enabled":{"type":"boolean","description":"Whether the rule evaluates (default true)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql. All other signals are computed over ROOT spans only: a service that fails on child spans but returns success from its entry point (cron jobs, workers; audit_setup STAT-04 lists them) reads healthy at any threshold. Use raw_query there or rely on error-issue notifications."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold (breach when `value threshold`). Ignored when a template is set."},"group_by":{"type":"array","items":{"type":"string"},"description":"Evaluate one value per group. Built-in tokens: service.name, span.name, status.code, http.method, severity; attribute keys as attr. (e.g. attr.http.route)."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window (default: 0). For raw_query it sums the `samples` column; without one each returned row counts as 1, so it gates on buckets, not events."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach, so a rule whose query stops matching opens an incident instead of going quiet (default false: such windows are skipped). Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting (default: 2)"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving (default: 2)"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes (default: 30)"},"apdex_threshold_ms":{"type":"number","description":"Response time counted as satisfactory, in ms. Required for signal_type=apdex."},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"The query to evaluate, in the query-builder draft shape dashboard custom-query widgets use ({ id, name, dataSource, aggregation, whereClause, groupBy, ... }). Required for signal_type=builder_query."},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column and optional `group` / `samples` columns (`samples` is the event count behind each row; it feeds minimum_sample_count). A query returning no rows is a no-data check, not a healthy one. Must reference $__orgFilter and $__timeFilter(col); $__startTime, $__endTime and $__interval_s are also available. At most 100 distinct groups. Required for signal_type=raw_query."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How several raw_query rows collapse into one value (default identity: the single row's value)."},"notification_title":{"type":"string","description":"Custom notification title template. Supports {{ variable }} substitution, e.g. \"{{ event.emoji }} {{ rule.name }}: {{ event.label }}\". Omit for the built-in format. Variables: rule.name, severity, signal.label, comparator.label, threshold, value, observed.summary, group, window, links.app, links.chat."},"notification_body":{"type":"string","description":"Custom notification body template (Markdown). Supports {{ variable }} substitution and {{#if key}}\u2026{{/if}} blocks, e.g. \"*Severity:* {{ severity }}\\n*Observed:* {{ observed.summary }}\". Omit for the built-in format."},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of destination_ids, paging step by step until acknowledged (from list_escalation_chains): its UUID or its REST id (esc_\u2026)"}},"required":["name","destination_ids"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Create Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, - {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or \"none\" to detach it from every tier. On a rule with warning and critical tiers an id sets the critical tier: its UUID or its REST id (esc_\u2026)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or \"none\" to detach. Applies to escalation_chain_tier; without it, an id sets the critical (or only) tier and \"none\" detaches every tier: its UUID or its REST id (esc_\u2026)"},"escalation_chain_tier":{"type":"string","enum":["warning","critical"],"description":"The tier escalation_chain_id sets or detaches, leaving the other tier's chain as it is"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_alert_rule","title":"Delete Alert Rule","description":"Permanently delete an alert rule together with its incident history, delivery events and evaluation state. Irreversible; requires confirm=true. Ids from list_alert_rules.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"confirm":{"type":"boolean","description":"Must be true. There is no undo."}},"required":["rule_id","confirm"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"annotations":{"title":"Delete Alert Rule","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleId","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id=\"none\" first; a tier that pages only through the chain needs a destination before that (destination_ids on a one-tier rule; the rule's thresholds via the v2 API or dashboard on a two-tier rule). Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, + {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it first with update_alert_rule escalation_chain_id=\"none\" and escalation_chain_tier naming the tier, so another tier's chain stays; a tier that pages only through the chain needs a destination before that (add_destination_ids for a one-tier rule or the warning tier; a critical tier via the rule's thresholds in the v2 API or dashboard). Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"get_dashboard","title":"Get Dashboard","description":"Full configuration of one dashboard: every widget with its id, visualization, dataSource, display, layout and any pinned timeRange. Widgets come back in the shape update_dashboard_widget and replace_dashboard_widgets take. Ids from list_dashboards.","inputSchema":{"type":"object","properties":{"dashboard_id":{"type":"string","description":"Dashboard ID: its UUID or its REST id (dash_\u2026)"}},"required":["dashboard_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"publicId":{"type":"string"},"dashboard":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"widgets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"visualization":{"type":"string","enum":["chart","hbar","stat","gauge","table","list","pie","histogram","heatmap","funnel","paths","markdown"]},"dataSource":{"$ref":"#/$defs/@maple~1WidgetDataSource"},"display":{"type":"object","properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chartPresentation":{"anyOf":[{"type":"object","properties":{"legend":{"anyOf":[{"type":"string","enum":["visible","hidden","right"]},{"type":"null"}]},"seriesStats":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tooltip":{"anyOf":[{"type":"string","enum":["visible","hidden"]},{"type":"null"}]},"showPoints":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fillNulls":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"boolean","enum":[false]},{"type":"null"}]},"compareToPreviousPeriod":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"xAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"yAxis":{"anyOf":[{"type":"object","properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMin":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"softMax":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScale":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"fitYAxisToData":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"visible":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"seriesMapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"colorOverrides":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"stacked":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"curveType":{"anyOf":[{"type":"string","enum":["linear","monotone"]},{"type":"null"}]},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}]},"sparkline":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"dataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1WidgetDataSource"},{"type":"null"}]}},"required":["enabled"],"additionalProperties":true},{"type":"null"}]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"header":{"type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"align":{"anyOf":[{"type":"string","enum":["left","center","right"]},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"thresholds":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"color":{"type":"string"}},"required":["value","color"],"additionalProperties":true}},{"type":"null"}]}},"required":["field","header"],"additionalProperties":true}},{"type":"null"}]},"listDataSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"listWhereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"listLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"listRootOnly":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"pie":{"anyOf":[{"type":"object","properties":{"donut":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"innerRadius":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"showLabels":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"showPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"funnel":{"anyOf":[{"type":"object","properties":{"showStepPercent":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["session"]},"dimension":{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},"value":{"type":"string"}},"required":["kind","dimension","value"],"additionalProperties":true}]}},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"breakdownBy":{"anyOf":[{"type":"string","enum":["referrerHost","utmSource","utmMedium","utmCampaign","country","host"]},{"type":"string","pattern":"^attribute:[\\s\\S]*?$"},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"variant":{"anyOf":[{"type":"string","enum":["bars","dropoff"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"paths":{"anyOf":[{"type":"object","properties":{"anchor":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["event"]},"eventName":{"type":"string"},"attributeEquals":{"type":"object","additionalProperties":{"type":"string"}}},"required":["kind","eventName"],"additionalProperties":true},{"type":"object","properties":{"kind":{"type":"string","enum":["page"]},"pagePath":{"type":"string"},"host":{"type":"string"}},"required":["kind","pagePath"],"additionalProperties":true}]},"direction":{"anyOf":[{"type":"string","enum":["after","before"]},{"type":"null"}]},"depth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"branches":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"keyBy":{"anyOf":[{"type":"string","enum":["person","visitor","user","session"]},{"type":"null"}]},"windowSeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"include":{"anyOf":[{"type":"string","enum":["all","events","pages"]},{"type":"null"}]},"exclude":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"filters":{"anyOf":[{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"pagePath":{"anyOf":[{"type":"string"},{"type":"null"}]},"referrerHost":{"anyOf":[{"type":"string"},{"type":"null"}]},"country":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceType":{"anyOf":[{"type":"string"},{"type":"null"}]},"browserName":{"anyOf":[{"type":"string"},{"type":"null"}]},"osName":{"anyOf":[{"type":"string"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmSource":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmMedium":{"anyOf":[{"type":"string"},{"type":"null"}]},"utmCampaign":{"anyOf":[{"type":"string"},{"type":"null"}]},"visitorType":{"anyOf":[{"type":"string","enum":["new","returning"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]}},"required":["anchor"],"additionalProperties":true},{"type":"null"}]},"histogram":{"anyOf":[{"type":"object","properties":{"bucketCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"bucketWidth":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"logScaleY":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"heatmap":{"anyOf":[{"type":"object","properties":{"colorScale":{"anyOf":[{"type":"string","enum":["amber","blues","reds","viridis","magma","cividis"]},{"type":"null"}]},"scaleType":{"anyOf":[{"type":"string","enum":["linear","log"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"gauge":{"anyOf":[{"type":"object","properties":{"min":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"max":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"style":{"anyOf":[{"type":"string","enum":["radial","bar"]},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"markdown":{"anyOf":[{"type":"object","properties":{"content":{"type":"string"}},"required":["content"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"layout":{"type":"object","properties":{"x":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"y":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"w":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"h":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"minW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"minH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxW":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"maxH":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]}},"required":["x","y","w","h"],"additionalProperties":true},"timeRange":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["relative"]},"value":{"type":"string"}},"required":["type","value"],"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string","enum":["absolute"]},"startTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"},"endTime":{"$ref":"#/$defs/@maple~1IsoDateTimeString"}},"required":["type","startTime","endTime"],"additionalProperties":true}]},"sectionId":{"type":"string"},"tabId":{"type":"string"}},"required":["id","visualization","dataSource","display","layout"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","tags","timeRange","widgets","createdAt","updatedAt"],"additionalProperties":true}},"required":["publicId","dashboard"],"additionalProperties":true,"$defs":{"@maple/IsoDateTimeString":{"type":"string"},"@maple/QueryWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["query"]},"resultShape":{"type":"string","enum":["timeseries","breakdown","list"]},"queries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"anyOf":[{"type":"string","enum":["default","meter"]},{"type":"null"}]},"metricName":{"anyOf":[{"type":"string"},{"type":"null"}]},"metricType":{"anyOf":[{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},{"type":"null"}]},"isMonotonic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"spaceAggregation":{"anyOf":[{"type":"string","enum":["sum","avg","min","max","count"]},{"type":"null"}]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"whereClause":{"anyOf":[{"type":"string"},{"type":"null"}]},"aggregation":{"type":"string"},"stepInterval":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderByDirection":{"anyOf":[{"type":"string","enum":["desc","asc"]},{"type":"null"}]},"addOns":{"anyOf":[{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":true},{"type":"null"}]},"groupBy":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"having":{"anyOf":[{"type":"string"},{"type":"null"}]},"orderBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"anyOf":[{"type":"string"},{"type":"null"}]},"seriesLimit":{"anyOf":[{"type":"string"},{"type":"null"}]},"legend":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":true}]}},"formulas":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expression":{"type":"string"},"legend":{"type":"string"},"hidden":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["id","name","expression","legend"],"additionalProperties":true}},{"type":"null"}]},"comparison":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"type":"string","enum":["none","previous_period"]},{"type":"null"}]},"includePercentChange":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":true},{"type":"null"}]},"defaultLimit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"columns":{"type":"array","items":{"type":"string"}},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","resultShape","queries"],"additionalProperties":true,"title":"Query widget data source"},"@maple/RawSqlWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["raw_sql"]},"sql":{"type":"string"},"displayType":{"type":"string"},"granularitySeconds":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","sql"],"additionalProperties":true,"title":"Raw SQL widget data source"},"@maple/RouteWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["route"]},"endpoint":{"type":"string"},"params":{"type":"object"},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind","endpoint"],"additionalProperties":true,"title":"Route widget data source"},"@maple/StaticWidgetDataSource":{"type":"object","properties":{"kind":{"type":"string","enum":["static"]},"transform":{"type":"object","properties":{"fieldMap":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"null"}]},"hideSeries":{"anyOf":[{"type":"object","properties":{"baseNames":{"type":"array","items":{"type":"string"}}},"required":["baseNames"],"additionalProperties":true},{"type":"null"}]},"flattenSeries":{"anyOf":[{"type":"object","properties":{"valueField":{"type":"string"}},"required":["valueField"],"additionalProperties":true},{"type":"null"}]},"reduceToValue":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"aggregate":{"anyOf":[{"type":"string","enum":["first","sum","count","avg","max","min"]},{"type":"null"}]}},"required":["field"],"additionalProperties":true},{"type":"null"}]},"computeRatio":{"anyOf":[{"type":"object","properties":{"numeratorName":{"type":"string"},"denominatorNames":{"type":"array","items":{"type":"string"}}},"required":["numeratorName","denominatorNames"],"additionalProperties":true},{"type":"null"}]},"limit":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sortBy":{"anyOf":[{"type":"object","properties":{"field":{"type":"string"},"direction":{"type":"string","enum":["asc","desc"]}},"required":["field","direction"],"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true}},"required":["kind"],"additionalProperties":true,"title":"Static widget data source"},"@maple/WidgetDataSource":{"anyOf":[{"$ref":"#/$defs/@maple~1QueryWidgetDataSource"},{"$ref":"#/$defs/@maple~1RawSqlWidgetDataSource"},{"$ref":"#/$defs/@maple~1RouteWidgetDataSource"},{"$ref":"#/$defs/@maple~1StaticWidgetDataSource"}],"title":"Widget data source"}}},"annotations":{"title":"Get Dashboard","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts b/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts index 9174752908..7409173afd 100644 --- a/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts +++ b/apps/ai/src/mcp/tools/__tests__/alert-tools.test.ts @@ -1093,6 +1093,29 @@ describe("alert tools", () => { }) expect(chainOf(tiered, { escalation_chain_id: CHAIN_ID })).toEqual([undefined, CHAIN_ID]) + // A named tier changes only that tier: detaching the warning chain keeps the critical one. + const bothChained = Schema.decodeSync(AlertRuleDocument)({ + ...tiered, + thresholds: Arr.map(tiered.thresholds, (tier) => ({ ...tier, escalationChainId: CHAIN_ID })), + }) + expect( + chainOf(bothChained, { escalation_chain_id: "none", escalation_chain_tier: "warning" }), + ).toEqual([null, CHAIN_ID]) + expect(chainOf(tiered, { escalation_chain_id: CHAIN_ID, escalation_chain_tier: "warning" })).toEqual([ + CHAIN_ID, + undefined, + ]) + const tierAlone = Effect.runSync( + Effect.flip(buildUpdatedRequest(tiered, { escalation_chain_tier: "warning" })), + ) + expect(tierAlone.parameter).toBe("escalation_chain_tier") + const missingTier = Effect.runSync( + Effect.flip( + buildUpdatedRequest(rule, { escalation_chain_id: "none", escalation_chain_tier: "warning" }), + ), + ) + expect(missingTier.parameter).toBe("escalation_chain_tier") + const malformed = Effect.runSync( Effect.flip(buildUpdatedRequest(rule, { escalation_chain_id: "not-a-chain" })), ) diff --git a/apps/ai/src/mcp/tools/delete-escalation-chain.ts b/apps/ai/src/mcp/tools/delete-escalation-chain.ts index 4fa933cd06..83ab8f47d8 100644 --- a/apps/ai/src/mcp/tools/delete-escalation-chain.ts +++ b/apps/ai/src/mcp/tools/delete-escalation-chain.ts @@ -12,7 +12,7 @@ export function registerDeleteEscalationChainTool(server: McpToolRegistrar) { name: "delete_escalation_chain", title: "Delete Escalation Chain", description: - 'Delete an escalation chain. Refused while a rule tier still names it: detach it with update_alert_rule escalation_chain_id="none" first; a tier that pages only through the chain needs a destination before that (destination_ids on a one-tier rule; the rule\'s thresholds via the v2 API or dashboard on a two-tier rule). ' + + "Delete an escalation chain. Refused while a rule tier still names it: detach it first with update_alert_rule escalation_chain_id=\"none\" and escalation_chain_tier naming the tier, so another tier's chain stays; a tier that pages only through the chain needs a destination before that (add_destination_ids for a one-tier rule or the warning tier; a critical tier via the rule's thresholds in the v2 API or dashboard). " + "Escalations already running finish. Ids from list_escalation_chains.", parameters: Schema.Struct({ chain_id: P.resourceId("escalationChain", "Escalation chain to delete"), diff --git a/apps/ai/src/mcp/tools/update-alert-rule.ts b/apps/ai/src/mcp/tools/update-alert-rule.ts index 62752b9e28..25db0e2240 100644 --- a/apps/ai/src/mcp/tools/update-alert-rule.ts +++ b/apps/ai/src/mcp/tools/update-alert-rule.ts @@ -105,7 +105,11 @@ export const UpdateAlertRuleParameters = Schema.Struct({ ), escalation_chain_id: P.optionalResourceId( "escalationChain", - 'Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or "none" to detach it from every tier. On a rule with warning and critical tiers an id sets the critical tier', + 'Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or "none" to detach. Applies to escalation_chain_tier; without it, an id sets the critical (or only) tier and "none" detaches every tier', + ), + escalation_chain_tier: P.optionalOneOf( + ["warning", "critical"], + "The tier escalation_chain_id sets or detaches, leaving the other tier's chain as it is", ), }) @@ -144,7 +148,7 @@ export const buildUpdatedRequest = Effect.fnUntraced(function* ( "escalation_chain_id", )(params.escalation_chain_id) const merged = yield* mergeThresholds(current, params, comparator) - const thresholds = withEscalationChain(merged, chain) + const thresholds = yield* withEscalationChain(current.name, merged, chain, params.escalation_chain_tier) return { name: params.name ?? current.name, @@ -241,16 +245,46 @@ const mergeThresholds = ( } /** Sets the chain on the only tier, or on the critical one of two; `undefined` keeps every tier's. */ +/** + * Sets or detaches a tier's chain. A named tier changes only that tier; without one, an id sets the + * critical (or only) tier and "none" detaches every tier. + */ const withEscalationChain = ( + ruleName: string, tiers: Arr.NonEmptyReadonlyArray, chain: EscalationChainId | null | undefined, -): Arr.NonEmptyReadonlyArray => { - if (chain === undefined) return tiers - // "none" detaches every tier's chain, wherever it was set. - if (chain === null) return Arr.map(tiers, (tier) => ({ ...tier, escalationChainId: null })) - const critical = Arr.findLastIndex(tiers, (tier) => tier.severity === "critical") + tier: AlertThreshold["severity"] | undefined, +): Effect.Effect, McpInvalidInputError> => { + if (chain === undefined) { + return tier === undefined + ? Effect.succeed(tiers) + : Effect.fail( + new McpInvalidInputError({ + message: + 'escalation_chain_tier needs escalation_chain_id (an id, or "none" to detach)', + parameter: "escalation_chain_tier", + }), + ) + } + if (tier !== undefined) { + if (!Arr.some(tiers, (t) => t.severity === tier)) { + return Effect.fail( + new McpInvalidInputError({ + message: `"${ruleName}" has no ${tier} tier`, + parameter: "escalation_chain_tier", + }), + ) + } + return Effect.succeed( + Arr.map(tiers, (t) => (t.severity === tier ? { ...t, escalationChainId: chain } : t)), + ) + } + if (chain === null) return Effect.succeed(Arr.map(tiers, (t) => ({ ...t, escalationChainId: null }))) + const critical = Arr.findLastIndex(tiers, (t) => t.severity === "critical") const target = tiers.length === 1 ? 0 : Option.getOrElse(critical, () => tiers.length - 1) - return Arr.map(tiers, (tier, index) => (index === target ? { ...tier, escalationChainId: chain } : tier)) + return Effect.succeed( + Arr.map(tiers, (t, index) => (index === target ? { ...t, escalationChainId: chain } : t)), + ) } /** `destination_ids` replaces; add/remove edit the current (or replaced) list in place. */ diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 58e0f284dc..06ae57746f 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -108,6 +108,7 @@ import { MutableHashMap, Option, Order, + Predicate, Random, Record, Result, @@ -2214,6 +2215,59 @@ export class AlertsService extends Context.Service, + ) { + const steps = Arr.filter( + rows, + (row) => + row.eventType === "trigger" && + row.incidentId !== null && + Predicate.hasProperty(row.payloadJson, "escalation"), + ) + const incidentIds = Arr.dedupe( + Arr.filterMap(steps, (row) => + row.incidentId === null ? Result.failVoid : Result.succeed(row.incidentId), + ), + ) + const orgIds = Arr.dedupe(Arr.map(steps, (row) => row.orgId)) + if (!Arr.isReadonlyArrayNonEmpty(incidentIds) || !Arr.isReadonlyArrayNonEmpty(orgIds)) { + return new Map() + } + const incidents = yield* dbExecute((db) => + db.run( + PG.from(AlertIncidents) + .select("id", "orgId", "status", "acknowledgedAt") + .where(($) => [$.orgId.in_(...orgIds), $.id.in_(...incidentIds)]), + ), + ) + const byKey = new Map( + Arr.map(incidents, (incident) => [`${incident.orgId}:${incident.id}`, incident]), + ) + const reasonOf = (row: AlertDeliveryEventRow): ReadonlyArray => { + const incident = byKey.get(`${row.orgId}:${row.incidentId}`) + if (incident === undefined) return [] + if (incident.status !== "open") { + return [ + [row.id, "Not sent: the incident resolved before this escalation step went out"], + ] + } + return incident.acknowledgedAt === null + ? [] + : [ + [ + row.id, + "Not sent: the incident was acknowledged before this escalation step went out", + ], + ] + } + return new Map(Arr.flatMap(steps, reasonOf)) + }) + const recordDeliveryFailure = Effect.fn("AlertsService.recordDeliveryFailure")(function* ( row: AlertDeliveryEventRow, currentTime: number, @@ -2458,32 +2512,11 @@ export class AlertsService extends Context.Service - db.run( - PG.from(AlertIncidents) - .select("status", "acknowledgedAt") - .where(($) => [$.orgId.eq(row.orgId), $.id.eq(stepIncidentId)]) - .limit(1), - ), - ))[0] ?? null) - : null - if ( - stepIncident !== null && - (stepIncident.status !== "open" || stepIncident.acknowledgedAt !== null) - ) { + // A queued escalation step goes out only to an incident still open and unacknowledged. + const staleStep = (yield* staleEscalationSteps([row])).get(row.id) + if (staleStep !== undefined) { yield* recordDeliveryFailure(row, claimTime, { - message: - stepIncident.status !== "open" - ? "Not sent: the incident resolved before this escalation step went out" - : "Not sent: the incident was acknowledged before this escalation step went out", + message: staleStep, kind: "escalation", retryable: false, }) @@ -2799,12 +2832,26 @@ export class AlertsService extends Context.Service count + claimed.length) - yield* Metric.update(AlertingMetrics.deliveriesAttemptedTotal, claimed.length) + // Escalation steps in the digest go out only for incidents still open and unacknowledged. + const staleSteps = yield* staleEscalationSteps(claimed) + yield* Effect.forEach( + Arr.filter(claimed, (row) => staleSteps.has(row.id)), + (row) => + recordDeliveryFailure(row, claimTime, { + message: staleSteps.get(row.id) ?? "Not sent", + kind: "escalation", + retryable: false, + }), + { discard: true }, + ) + const sendable = Arr.filter(claimed, (row) => !staleSteps.has(row.id)) + if (sendable.length === 0) return + yield* Ref.update(processedCount, (count) => count + sendable.length) + yield* Metric.update(AlertingMetrics.deliveriesAttemptedTotal, sendable.length) yield* Effect.annotateCurrentSpan({ "maple.delivery.destination_id": destinationRow.id, "maple.delivery.destination_type": "chat", - "maple.delivery.digest_size": claimed.length, + "maple.delivery.digest_size": sendable.length, }) yield* Effect.gen(function* () { const hydrated = yield* hydrateDestination(destinationRow) @@ -2814,7 +2861,7 @@ export class AlertsService extends Context.Service Effect.forEach( - claimed, + sendable, (row, index) => recoverDeliveryFailure(row, error, index === 0).pipe( isolate("Alert delivery recovery failed", { @@ -2836,14 +2883,14 @@ export class AlertsService extends Context.Service - finalizeDigest(destinationRow, claimed, claimTime, { - providerMessage: `Delivered to ${posted.connectorName} #${channelName} in a digest of ${claimed.length}`, + finalizeDigest(destinationRow, sendable, claimTime, { + providerMessage: `Delivered to ${posted.connectorName} #${channelName} in a digest of ${sendable.length}`, providerReference: posted.messageId, }), }), Effect.catchDefect((defect) => Effect.forEach( - claimed, + sendable, (row) => recoverDeliveryDefect(row, defect).pipe( isolate("Alert delivery recovery failed", { From fb84b4c6dbfc80a8283a5986d3e9800bce020c13 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 14:56:09 +0100 Subject: [PATCH 23/29] fix(alerts): a destination a running escalation still pages cannot be deleted --- .../alerts/AlertDestinationsService.ts | 26 +++++++++++++---- .../alerts/AlertsService.escalation.test.ts | 29 +++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/services/alerts/AlertDestinationsService.ts b/packages/backend/src/services/alerts/AlertDestinationsService.ts index 2b692e72ab..8314111cce 100644 --- a/packages/backend/src/services/alerts/AlertDestinationsService.ts +++ b/packages/backend/src/services/alerts/AlertDestinationsService.ts @@ -31,6 +31,7 @@ import * as Orm from "@maple-dev/effect-orm/database" import * as PG from "@maple-dev/effect-orm/postgres" import { AlertDestinations, + AlertEscalationRuns, AlertLinearIssues, AlertRules, EscalationChains, @@ -1066,12 +1067,25 @@ export class AlertDestinationsService extends Context.Service< ])}::jsonb`, ]), ) - if (chains.length > 0) { - return { - referencedBy: [], - chainNames: Arr.map(chains, (chain) => chain.name), - deleted: [], - } + // A running escalation pages from its own snapshot, so a chain edited or deleted since + // can still name the destination until the run ends. + const runs = yield* db.run( + PG.from(AlertEscalationRuns) + .select("chainName") + .where(($) => [ + $.orgId.eq(orgId), + $.status.in_("active", "acknowledged"), + PG.sql.cond`${$.stepsJson} @> ${encodeJson([ + { targets: [{ type: "destination", destinationId }] }, + ])}::jsonb`, + ]), + ) + const usedBy = Arr.dedupe([ + ...Arr.map(chains, (chain) => chain.name), + ...Arr.map(runs, (run) => `${run.chainName} (escalating an open incident)`), + ]) + if (usedBy.length > 0) { + return { referencedBy: [], chainNames: usedBy, deleted: [] } } const deleted = yield* db.run( PG.deleteFrom(AlertDestinations) diff --git a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts index c1d2d28314..61e75dd01e 100644 --- a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts +++ b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts @@ -5,6 +5,7 @@ import { EscalationChainCreateRequest, EscalationChainId, EscalationChainInUseError, + EscalationChainUpdateRequest, WebhookAlertDestinationConfig, type AlertDestinationId, type AlertThreshold, @@ -495,6 +496,34 @@ describe("escalation chains", () => { }).pipe(Effect.provide(layerFor(testDb, state, requests))) }) + it.effect("refuses deleting a destination a running escalation still pages from its snapshot", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, chains, orgId, userId, slack, pager, chain } = + yield* setup("org_esc_snapshot_guard") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + // The chain stops naming slack, but the open incident's run still has it in its snapshot. + yield* chains.update( + orgId, + userId, + adminRoles, + chain.id, + new EscalationChainUpdateRequest({ + steps: [{ targets: [{ type: "destination", destinationId: pager.id }], waitMinutes: 10 }], + }), + ) + const refused = getError( + yield* Effect.exit(alerts.deleteDestination(orgId, adminRoles, slack.id)), + ) + if (!Schema.is(AlertDestinationInUseError)(refused)) return assert.fail("expected in use") + assert.include(refused.message, "escalating an open incident") + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + it.effect("refuses a rule naming an unknown chain, and deleting a chain or destination in use", () => { const testDb = createTestDb(trackedDbs) const state = { tracesAggregateRows: errorRateRows(10) } From de6959d35d5fdaf35c8803bad69dd9d20e917e64 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 16:08:05 +0100 Subject: [PATCH 24/29] fix(api): v2 escalation chain step errors say what is wrong in wire terms An empty or oversized steps or targets array returned 400 "Expected ". The v2 schemas now carry messages, so the error names the field (steps[0].targets) and the limit. --- apps/api/src/routes/v2/alerts.http.test.ts | 42 +++++++++++++++++++ .../domain/src/http/v2/escalation-chains.ts | 17 ++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/apps/api/src/routes/v2/alerts.http.test.ts b/apps/api/src/routes/v2/alerts.http.test.ts index cb29d3e9c9..071f344d38 100644 --- a/apps/api/src/routes/v2/alerts.http.test.ts +++ b/apps/api/src/routes/v2/alerts.http.test.ts @@ -1485,4 +1485,46 @@ describe("v2 alerts over HTTP", () => { await harness.dispose() }) + + it("names the step field and what is wrong when escalation chain steps are empty or too long", async () => { + const harness = makeHarness() + const key = await harness.bootstrapKey(["alerts:write"]) + const primary = await createWebhook(harness, key.secret, "primary") + const target = { type: "destination", destination_id: primary.body.id } + const step = { targets: [target], wait_minutes: 10 } + const created = await harness.request("POST", "/v2/alerts/escalation_chains", key.secret, { + name: "Paging", + steps: [step], + }) + expect(created.status, JSON.stringify(created.body)).toBe(200) + const post = (body: unknown) => + harness.request("POST", "/v2/alerts/escalation_chains", key.secret, body) + const patch = (body: unknown) => + harness.request("PATCH", `/v2/alerts/escalation_chains/${created.body.id}`, key.secret, body) + + const responses = await Promise.all([ + post({ name: "Empty", steps: [] }), + post({ name: "No targets", steps: [{ targets: [], wait_minutes: 10 }] }), + post({ name: "Long", steps: Arr.makeBy(11, () => step) }), + post({ name: "Wide", steps: [{ targets: Arr.makeBy(11, () => target), wait_minutes: 10 }] }), + patch({ steps: [] }), + patch({ steps: [step, { targets: [], wait_minutes: 5 }] }), + ]) + expect( + Arr.map(responses, (response) => [ + response.status, + response.body.error.param, + response.body.error.message, + ]), + ).toEqual([ + [400, "steps", "steps: needs at least one step"], + [400, "steps[0].targets", "steps[0].targets: needs at least one target"], + [400, "steps", "steps: allows at most 10 steps"], + [400, "steps[0].targets", "steps[0].targets: allows at most 10 targets per step"], + [400, "steps", "steps: needs at least one step"], + [400, "steps[1].targets", "steps[1].targets: needs at least one target"], + ]) + + await harness.dispose() + }) }) diff --git a/packages/domain/src/http/v2/escalation-chains.ts b/packages/domain/src/http/v2/escalation-chains.ts index 786633b7d9..52c8a4a3db 100644 --- a/packages/domain/src/http/v2/escalation-chains.ts +++ b/packages/domain/src/http/v2/escalation-chains.ts @@ -37,9 +37,15 @@ export type V2EscalationTarget = Schema.Schema.Type // Bounded arrays on the wire, not tuples: generated clients do not all read `prefixItems`. // The refine narrows the decoded type to non-empty and goes first, so the checks carry the docs. +// Messages are wire terms: the v2 boundary prefixes them with the field path. const V2EscalationTargets = Schema.Array(V2EscalationTarget) - .pipe(Schema.refine(Arr.isReadonlyArrayNonEmpty)) - .check(Schema.isMinLength(1), Schema.isMaxLength(MAX_ESCALATION_STEP_TARGETS)) + .pipe(Schema.refine(Arr.isReadonlyArrayNonEmpty, { message: "needs at least one target" })) + .check( + Schema.isMinLength(1), + Schema.isMaxLength(MAX_ESCALATION_STEP_TARGETS, { + message: `allows at most ${MAX_ESCALATION_STEP_TARGETS} targets per step`, + }), + ) export const V2EscalationStep = Schema.Struct({ targets: V2EscalationTargets.annotate({ @@ -57,8 +63,11 @@ export const V2EscalationStep = Schema.Struct({ export type V2EscalationStep = Schema.Schema.Type const V2EscalationSteps = Schema.Array(V2EscalationStep) - .pipe(Schema.refine(Arr.isReadonlyArrayNonEmpty)) - .check(Schema.isMinLength(1), Schema.isMaxLength(MAX_ESCALATION_STEPS)) + .pipe(Schema.refine(Arr.isReadonlyArrayNonEmpty, { message: "needs at least one step" })) + .check( + Schema.isMinLength(1), + Schema.isMaxLength(MAX_ESCALATION_STEPS, { message: `allows at most ${MAX_ESCALATION_STEPS} steps` }), + ) .annotate({ description: `The steps in order, 1 to ${MAX_ESCALATION_STEPS}.` }) const RepeatCount = EscalationRepeatCount.annotate({ From 254950ee538a2c4f00456cf75dc6165518258ecd Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 16:08:05 +0100 Subject: [PATCH 25/29] fix(alerts): destination in-use conflict names escalation chains too Deleting a destination only an escalation chain uses returned a 409 that blamed alert rules. --- apps/api/src/routes/v2/alerts.http.test.ts | 26 ++++++++++++++++++++++ packages/domain/src/http/alerts.ts | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/api/src/routes/v2/alerts.http.test.ts b/apps/api/src/routes/v2/alerts.http.test.ts index 071f344d38..c2f95c4e43 100644 --- a/apps/api/src/routes/v2/alerts.http.test.ts +++ b/apps/api/src/routes/v2/alerts.http.test.ts @@ -1527,4 +1527,30 @@ describe("v2 alerts over HTTP", () => { await harness.dispose() }) + + it("refuses to delete a destination only an escalation chain uses, naming chains in the conflict", async () => { + const harness = makeHarness() + const key = await harness.bootstrapKey(["alerts:write"]) + const primary = await createWebhook(harness, key.secret, "primary") + const chain = await harness.request("POST", "/v2/alerts/escalation_chains", key.secret, { + name: "Chain only", + steps: [ + { targets: [{ type: "destination", destination_id: primary.body.id }], wait_minutes: 10 }, + ], + }) + expect(chain.status, JSON.stringify(chain.body)).toBe(200) + + const conflicted = await harness.request( + "DELETE", + `/v2/alerts/destinations/${primary.body.id}`, + key.secret, + ) + expect([conflicted.status, conflicted.body.error.code, conflicted.body.error.message]).toEqual([ + 409, + "alert_destination_in_use", + "The alert destination is currently used by one or more alert rules or escalation chains.", + ]) + + await harness.dispose() + }) }) diff --git a/packages/domain/src/http/alerts.ts b/packages/domain/src/http/alerts.ts index a1655a0545..3898a8a951 100644 --- a/packages/domain/src/http/alerts.ts +++ b/packages/domain/src/http/alerts.ts @@ -1489,7 +1489,7 @@ export class AlertDestinationInUseError extends HttpTaggedError Date: Fri, 6 Nov 2026 16:08:14 +0100 Subject: [PATCH 26/29] fix(mcp): escalation chain tools report bad fields on their parameter and show REST ids in list create/update_escalation_chain mapped every request decode failure to steps; a bad repeat_count, name or description now names that parameter in tool spelling. list_escalation_chains renders the same esc_ and dest_ ids as create, and target rows carry destinationPublicId. --- apps/ai/src/mcp/lib/escalation-chains.ts | 52 ++++++++++++++++--- apps/ai/src/mcp/tool-descriptors.generated.ts | 6 +-- .../__tests__/escalation-chain-tools.test.ts | 52 +++++++++++++++++-- .../src/mcp/tools/create-escalation-chain.ts | 13 ++--- .../src/mcp/tools/list-escalation-chains.ts | 14 +---- .../src/mcp/tools/update-escalation-chain.ts | 11 +--- .../src/mcp-outputs/escalation-chains.ts | 2 + 7 files changed, 106 insertions(+), 44 deletions(-) diff --git a/apps/ai/src/mcp/lib/escalation-chains.ts b/apps/ai/src/mcp/lib/escalation-chains.ts index bb4067e608..0a24b89704 100644 --- a/apps/ai/src/mcp/lib/escalation-chains.ts +++ b/apps/ai/src/mcp/lib/escalation-chains.ts @@ -1,5 +1,5 @@ /** Pieces the escalation chain MCP tools share: rows, step text, step input and error mapping. */ -import { Array as Arr, Effect, Option, Schema } from "effect" +import { Array as Arr, Effect, Option, Schema, SchemaIssue } from "effect" import type { AlertDestinationDocument, AlertForbiddenError, @@ -73,6 +73,7 @@ export const toEscalationChainRow = ( ) return { destinationId: target.destinationId, + destinationPublicId: encodePublicId(PublicIdPrefixes.alertDestination, target.destinationId), name: known?.name ?? null, type: known?.type ?? null, } @@ -94,13 +95,13 @@ const formatWait = (minutes: number): string => const targetLabel = (target: ChainRow["steps"][number]["targets"][number]): string => target.name === null - ? `destination ${target.destinationId}` + ? `destination ${target.destinationPublicId}` : target.type === null - ? target.name - : `${target.name} (${target.type})` + ? `${target.name} (${target.destinationPublicId})` + : `${target.name} (${target.type}, ${target.destinationPublicId})` -/** One line per step, e.g. `1. notify On-call (pagerduty), then wait 10m for an ack`. */ -export const formatSteps = (chain: ChainRow): string => +/** One line per step, e.g. `1. notify On-call (pagerduty, dest_…), then wait 10m for an ack`. */ +const formatSteps = (chain: ChainRow): string => Arr.join( Arr.map( chain.steps, @@ -110,7 +111,7 @@ export const formatSteps = (chain: ChainRow): string => "\n", ) -export const formatRepeats = (repeatCount: number): string => +const formatRepeats = (repeatCount: number): string => repeatCount === 0 ? "runs once" : `repeats ${repeatCount} more time${repeatCount === 1 ? "" : "s"}` export const chainBlocks = (chain: ChainRow) => [ @@ -131,6 +132,43 @@ export const chainNotFound = (chainId: string) => parameter: "chain_id", }) +const formatIssues = SchemaIssue.makeFormatterStandardSchemaV1() + +/** A service request key as the tool spells it: a parameter, or a key inside `steps`. */ +const toolKey = (segment: PropertyKey): string => { + const key = String(segment) + switch (key) { + case "repeatCount": + return "repeat_count" + case "waitMinutes": + return "wait_minutes" + case "targets": + return "destination_ids" + default: + return key + } +} + +/** A request that failed to decode, reported against the parameter its first issue is under. */ +export const invalidChainRequest = (error: Schema.SchemaError): McpInvalidInputError => { + const first = formatIssues(error.issue).issues[0] + const path = Arr.map(first?.path ?? [], (segment) => + typeof segment === "object" && segment !== null ? segment.key : segment, + ) + const [root] = path + const location = Arr.join( + Arr.map(path, (segment, index) => + typeof segment === "number" ? `[${segment}]` : `${index === 0 ? "" : "."}${toolKey(segment)}`, + ), + "", + ) + const message = first?.message ?? String(error) + return new McpInvalidInputError({ + message: location === "" ? `Invalid escalation chain: ${message}` : `${location}: ${message}`, + parameter: root === undefined ? "steps" : toolKey(root), + }) +} + const failWith = (pipeName: string) => (error: AlertForbiddenError | AlertPersistenceError) => Effect.fail(toMcpHttpError(pipeName)(error)) diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 4424830019..8f64ca2dd8 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -38,9 +38,9 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"update_alert_rule","title":"Update Alert Rule","description":"Update an alert rule. Pass only the fields to change; the rest keep their current value (get_alert_rule shows it). rule_ids applies one change to many rules (e.g. add_destination_ids on every rule with rule_ids=[\"*\"]). Ids from list_alert_rules and list_alert_destinations.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID to update (use list_alert_rules to find IDs): its UUID or its REST id (alrt_\u2026)"},"rule_ids":{"type":"array","items":{"type":"string"},"description":"Apply the same change to several rules in one call instead of rule_id; [\"*\"] means every rule. name cannot be set in bulk. Each is its UUID or its REST id (alrt_\u2026)"},"name":{"type":"string","description":"New rule name"},"severity":{"type":"string","enum":["warning","critical"],"description":"Alert severity"},"threshold":{"type":"number","description":"Threshold value. E.g. 0.05 for 5% error rate, 1000 for 1s latency"},"window_minutes":{"type":"number","description":"Evaluation window in minutes, at most 1440"},"evaluation_interval_minutes":{"anyOf":[{"type":"number","enum":[1,5,10,15,30,60]},{"type":"string"},{"type":"string","enum":["auto"]}],"description":"Minutes between checks, at most window_minutes. A raw_query rule checked less often gets a longer query time limit: 5s under 5 min, 15s at 5, 25s at 10 or more. Omit to keep the current one; pass \"auto\" to derive it from the window (a fifth, 1 to 5). Any edit also clears a slowdown the scheduler applied."},"services":{"type":"array","items":{"type":"string"},"description":"Service scope (replaces the current one; an empty list means all services)"},"environments":{"type":"array","items":{"type":"string"},"description":"Deployment environments to scope the alert to (replaces the current scope; pass an empty list for all environments). Ignored for builder_query / raw_query."},"enabled":{"type":"boolean","description":"Whether the rule is enabled"},"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to notify, from list_alert_destinations; replaces the current ones. A rule needs at least one. Each is its UUID or its REST id (dest_\u2026)"},"add_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to add, keeping the current ones. Each is its UUID or its REST id (dest_\u2026)"},"remove_destination_ids":{"type":"array","items":{"type":"string"},"description":"Destination IDs to remove, keeping the rest. Each is its UUID or its REST id (dest_\u2026)"},"signal_type":{"type":"string","enum":["error_rate","p95_latency","p99_latency","apdex","throughput","builder_query","raw_query"],"description":"What the rule measures. builder_query takes query_builder_draft, raw_query takes raw_query_sql."},"comparator":{"type":"string","enum":["gt","gte","lt","lte","eq","neq"],"description":"How the observed value is compared with threshold"},"group_by":{"type":"array","items":{"type":"string"},"description":"Dimensions to evaluate the alert per-group (replaces the current grouping; an empty list removes it). Built-in tokens: service.name, span.name, status.code, http.method, severity. Attribute keys: attr.."},"minimum_sample_count":{"type":"number","description":"Skip evaluation below this many samples in the window. For raw_query it sums the `samples` column; without one each returned row counts as 1."},"alert_on_no_data":{"type":"boolean","description":"Count a window with no data as a breach instead of skipping it. Not supported with group_by."},"consecutive_breaches":{"type":"number","description":"Consecutive breaches before alerting"},"consecutive_healthy":{"type":"number","description":"Consecutive healthy evaluations before resolving"},"renotify_interval_minutes":{"type":"number","description":"Re-notification interval in minutes"},"apdex_threshold_ms":{"type":"number","description":"Apdex threshold in milliseconds (for signal_type=apdex)"},"query_builder_draft":{"type":"string","contentMediaType":"application/json","contentSchema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["traces"]},"valueField":{"type":"string"}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["logs"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["metrics"]},"signalSource":{"type":"string","enum":["default","meter"]},"metricName":{"type":"string"},"metricType":{"type":"string","enum":["sum","gauge","histogram","exponential_histogram"]},"isMonotonic":{"type":"boolean"},"spaceAggregation":{"type":"string","enum":["sum","avg","min","max","count"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"hidden":{"type":"boolean"},"whereClause":{"type":"string"},"aggregation":{"type":"string"},"stepInterval":{"type":"string"},"orderByDirection":{"type":"string","enum":["desc","asc"]},"addOns":{"type":"object","properties":{"groupBy":{"type":"boolean"},"having":{"type":"boolean"},"orderBy":{"type":"boolean"},"limit":{"type":"boolean"},"legend":{"type":"boolean"}},"required":["groupBy","having","orderBy","limit","legend"],"additionalProperties":false},"groupBy":{"type":"array","items":{"type":"string"}},"having":{"type":"string"},"orderBy":{"type":"string"},"limit":{"type":"string"},"seriesLimit":{"type":"string"},"legend":{"type":"string"},"dataSource":{"type":"string","enum":["product_events"]}},"required":["id","name","aggregation","dataSource"],"additionalProperties":false}]},"description":"Query-builder draft, same shape as create_alert_rule (for signal_type=builder_query)"},"raw_query_sql":{"type":"string","description":"ClickHouse SQL returning a numeric `value` column (for signal_type=raw_query), at most 100 groups. Must reference $__orgFilter and $__timeFilter(col)."},"raw_query_reducer":{"type":"string","enum":["identity","sum","avg","max","min"],"description":"How to collapse raw_query result rows into one value."},"notification_title":{"type":"string","description":"Notification title template with {{ variable }} substitution; variables as in create_alert_rule"},"notification_body":{"type":"string","description":"Notification body template (Markdown) with {{ variable }} and {{#if key}}...{{/if}}"},"escalation_chain_id":{"type":"string","description":"Escalation chain an incident starts on top of the destinations (from list_escalation_chains), or \"none\" to detach. Applies to escalation_chain_tier; without it, an id sets the critical (or only) tier and \"none\" detaches every tier: its UUID or its REST id (esc_\u2026)"},"escalation_chain_tier":{"type":"string","enum":["warning","critical"],"description":"The tier escalation_chain_id sets or detaches, leaving the other tier's chain as it is"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"rule":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"severity":{"type":"string"},"serviceNames":{"type":"array","items":{"type":"string"}},"environments":{"type":"array","items":{"type":"string"}},"signalType":{"type":"string"},"comparator":{"type":"string"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"windowMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"evaluationIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"slowedIntervalMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"destinationIds":{"type":"array","items":{"type":"string"}},"escalationChains":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"chainId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["severity","chainId"],"additionalProperties":true}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","enabled","severity","serviceNames","environments","signalType","comparator","threshold","windowMinutes","evaluationIntervalMinutes","slowedIntervalMinutes","destinationIds","createdAt","updatedAt"],"additionalProperties":true}},"warnings":{"type":"array","items":{"type":"string"}},"evaluation":{"type":"object","properties":{"nextEvaluationAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"window":{"type":"string"},"status":{"type":"string"},"skipReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"value":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]},{"type":"null"}]},"sampleCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["groupKey","window","status","skipReason","value","sampleCount"],"additionalProperties":true}},"previewError":{"type":"string"}},"required":["nextEvaluationAt","current"],"additionalProperties":true}},"required":["rule"],"additionalProperties":true},"annotations":{"title":"Update Alert Rule","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_alert_rule","title":"Delete Alert Rule","description":"Permanently delete an alert rule together with its incident history, delivery events and evaluation state. Irreversible; requires confirm=true. Ids from list_alert_rules.","inputSchema":{"type":"object","properties":{"rule_id":{"type":"string","description":"Alert rule ID: its UUID or its REST id (alrt_\u2026)"},"confirm":{"type":"boolean","description":"Must be true. There is no undo."}},"required":["rule_id","confirm"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true},"annotations":{"title":"Delete Alert Rule","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleId","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, - {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, - {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"destinationPublicId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","destinationPublicId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"destinationPublicId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","destinationPublicId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, + {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"destinationPublicId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","destinationPublicId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it first with update_alert_rule escalation_chain_id=\"none\" and escalation_chain_tier naming the tier, so another tier's chain stays; a tier that pages only through the chain needs a destination before that (add_destination_ids for a one-tier rule or the warning tier; a critical tier via the rule's thresholds in the v2 API or dashboard). Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts b/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts index fc8168b302..9423e8073c 100644 --- a/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts +++ b/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts @@ -169,11 +169,57 @@ describe("escalation chain tools", () => { const output = Schema.decodeUnknownSync(ListEscalationChainsOutput)(result.structuredContent) expect(output.chains[0]?.publicId).toBe(encodePublicId("esc", CHAIN_ID)) expect(output.chains[0]?.steps[1]?.targets[0]?.name).toBe("Ops channel") - expect(text(result)).toContain("1. notify Primary pager (pagerduty), then wait 10m for an ack") - expect(text(result)).toContain("2. notify Ops channel (webhook), then wait 1h 30m for an ack") + expect(text(result)).toContain( + `1. notify Primary pager (pagerduty, ${encodePublicId("dest", PRIMARY)}), then wait 10m for an ack`, + ) + expect(text(result)).toContain( + `2. notify Ops channel (webhook, ${encodePublicId("dest", SECONDARY)}), then wait 1h 30m for an ack`, + ) expect(text(result)).toContain("repeats 1 more time") }) + it("list_escalation_chains shows the esc_ and dest_ ids create shows", async () => { + const listed = await ok("list_escalation_chains", {}) + const created = await ok("create_escalation_chain", { + name: "Checkout on-call", + steps: JSON.stringify([{ destination_ids: [PRIMARY], wait_minutes: 10 }]), + }) + const output = Schema.decodeUnknownSync(ListEscalationChainsOutput)(listed.structuredContent) + expect(output.chains[0]?.steps[0]?.targets[0]?.destinationPublicId).toBe( + encodePublicId("dest", PRIMARY), + ) + for (const result of [listed, created]) { + expect(text(result)).toContain(encodePublicId("esc", CHAIN_ID)) + expect(text(result)).toContain(CHAIN_ID) + expect(text(result)).toContain(encodePublicId("dest", PRIMARY)) + } + }) + + it("create and update report a bad field against the parameter it came from", async () => { + const step = [{ destination_ids: [PRIMARY], wait_minutes: 10 }] + const failures = await Promise.all([ + run("create_escalation_chain", { name: "Loop", steps: step, repeat_count: 6 }, {}), + run("create_escalation_chain", { name: " padded ", steps: step }, {}), + run("create_escalation_chain", { name: "Notes", steps: step, description: "x".repeat(501) }, {}), + run("create_escalation_chain", { name: "Slow", steps: [{ ...step[0], wait_minutes: 0 }] }, {}), + run("update_escalation_chain", { chain_id: CHAIN_ID, repeat_count: 6 }, {}), + run("update_escalation_chain", { chain_id: CHAIN_ID, name: "x".repeat(121) }, {}), + ]) + expect(Arr.map(failures, invalidParameter)).toEqual([ + "repeat_count", + "name", + "description", + "steps", + "repeat_count", + "name", + ]) + const messages = Arr.map(failures, (result) => + result._tag === "Failure" ? result.failure.message : "", + ) + expect(messages[0]).toContain("repeat_count: Expected a value less than or equal to 5") + expect(messages[3]).toContain("steps[0].wait_minutes: Expected a value greater than or equal to 1") + }) + it("create_escalation_chain decodes REST destination ids and JSON steps into the request", async () => { const seen: Seen = {} const result = await ok( @@ -200,7 +246,7 @@ describe("escalation chain tools", () => { ]) expect(seen.created?.repeatCount).toBe(2) expect(text(result)).toContain( - "2. notify Ops channel (webhook), Primary pager (pagerduty), then wait 15m", + `2. notify Ops channel (webhook, ${encodePublicId("dest", SECONDARY)}), Primary pager (pagerduty, ${encodePublicId("dest", PRIMARY)}), then wait 15m`, ) }) diff --git a/apps/ai/src/mcp/tools/create-escalation-chain.ts b/apps/ai/src/mcp/tools/create-escalation-chain.ts index f1742a689d..8e352fea75 100644 --- a/apps/ai/src/mcp/tools/create-escalation-chain.ts +++ b/apps/ai/src/mcp/tools/create-escalation-chain.ts @@ -1,4 +1,4 @@ -import { McpInvalidInputError, type McpToolRegistrar } from "./types" +import type { McpToolRegistrar } from "./types" import { Effect, Schema } from "effect" import { EscalationChainCreateRequest } from "@maple/domain/http" import { EscalationChainMutationOutput } from "@maple/domain/mcp-outputs" @@ -8,6 +8,7 @@ import { chainBlocks, createChainErrors, destinationDirectory, + invalidChainRequest, StepsInput, stepsParameterDescription, toEscalationChainRow, @@ -44,15 +45,7 @@ export function registerCreateEscalationChainTool(server: McpToolRegistrar) { steps, ...(params.repeat_count === undefined ? undefined : { repeatCount: params.repeat_count }), ...(params.description === undefined ? undefined : { description: params.description }), - }).pipe( - Effect.mapError( - (error) => - new McpInvalidInputError({ - message: `Invalid escalation chain: ${String(error)}`, - parameter: "steps", - }), - ), - ) + }).pipe(Effect.mapError(invalidChainRequest)) const tenant = yield* CurrentMcpTenant const chains = yield* EscalationChainsService const chain = yield* chains diff --git a/apps/ai/src/mcp/tools/list-escalation-chains.ts b/apps/ai/src/mcp/tools/list-escalation-chains.ts index 11594e75b0..50b49ad295 100644 --- a/apps/ai/src/mcp/tools/list-escalation-chains.ts +++ b/apps/ai/src/mcp/tools/list-escalation-chains.ts @@ -4,12 +4,7 @@ import { ListEscalationChainsOutput } from "@maple/domain/mcp-outputs" import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" import { CurrentMcpTenant } from "../lib/query-warehouse" import { toMcpHttpError } from "../lib/map-http-error" -import { - destinationDirectory, - formatRepeats, - formatSteps, - toEscalationChainRow, -} from "../lib/escalation-chains" +import { chainBlocks, destinationDirectory, toEscalationChainRow } from "../lib/escalation-chains" import { doc } from "../lib/tool-doc" export function registerListEscalationChainsTool(server: McpToolRegistrar) { @@ -49,12 +44,7 @@ export function registerListEscalationChainsTool(server: McpToolRegistrar) { }, } : undefined), - blocks: Arr.flatMap(output.chains, (chain) => [ - doc.heading(`${chain.name} (${chain.id})`), - doc.text( - `${formatSteps(chain)}\nThe chain ${formatRepeats(chain.repeatCount)}. Used by ${chain.ruleIds.length} rule${chain.ruleIds.length === 1 ? "" : "s"}.`, - ), - ]), + blocks: Arr.flatMap(output.chains, (chain) => [doc.heading(chain.name), ...chainBlocks(chain)]), next: output.chains.length === 0 ? [doc.next("list_alert_destinations", {}, "the destinations a step can notify")] diff --git a/apps/ai/src/mcp/tools/update-escalation-chain.ts b/apps/ai/src/mcp/tools/update-escalation-chain.ts index df29eb0a9a..801e988672 100644 --- a/apps/ai/src/mcp/tools/update-escalation-chain.ts +++ b/apps/ai/src/mcp/tools/update-escalation-chain.ts @@ -7,6 +7,7 @@ import { CurrentMcpTenant } from "../lib/query-warehouse" import { chainBlocks, destinationDirectory, + invalidChainRequest, StepsInput, stepsParameterDescription, toEscalationChainRow, @@ -54,15 +55,7 @@ export function registerUpdateEscalationChainTool(server: McpToolRegistrar) { ...(steps === undefined ? undefined : { steps }), ...(params.repeat_count === undefined ? undefined : { repeatCount: params.repeat_count }), ...(params.description === undefined ? undefined : { description: params.description }), - }).pipe( - Effect.mapError( - (error) => - new McpInvalidInputError({ - message: `Invalid escalation chain: ${String(error)}`, - parameter: "steps", - }), - ), - ) + }).pipe(Effect.mapError(invalidChainRequest)) const tenant = yield* CurrentMcpTenant const chains = yield* EscalationChainsService const chain = yield* chains diff --git a/packages/domain/src/mcp-outputs/escalation-chains.ts b/packages/domain/src/mcp-outputs/escalation-chains.ts index f36193ef1f..a8ac177161 100644 --- a/packages/domain/src/mcp-outputs/escalation-chains.ts +++ b/packages/domain/src/mcp-outputs/escalation-chains.ts @@ -5,6 +5,8 @@ const NullableString = Schema.NullOr(Schema.String) export const EscalationTargetRow = Schema.Struct({ destinationId: Schema.String, + /** The same destination's id in the REST API (`dest_…`). */ + destinationPublicId: Schema.String, /** Null when the destination list could not be read or the destination is gone. */ name: NullableString, type: NullableString, From 2c1ffc630d5bb24f60cc8f0d21e526ac07c5bcd2 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 16:19:51 +0100 Subject: [PATCH 27/29] fix(mcp): update_escalation_chain clears notes with description "none" --- apps/ai/src/mcp/tool-descriptors.generated.ts | 2 +- .../tools/__tests__/escalation-chain-tools.test.ts | 8 ++++++++ apps/ai/src/mcp/tools/update-escalation-chain.ts | 14 +++++++++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/ai/src/mcp/tool-descriptors.generated.ts b/apps/ai/src/mcp/tool-descriptors.generated.ts index 8f64ca2dd8..4b215f94d5 100644 --- a/apps/ai/src/mcp/tool-descriptors.generated.ts +++ b/apps/ai/src/mcp/tool-descriptors.generated.ts @@ -40,7 +40,7 @@ export const MCP_TOOL_DESCRIPTORS: ReadonlyArray = [ {"name":"acknowledge_alert_incident","title":"Acknowledge Alert Incident","description":"Mark an open alert incident as acknowledged (someone is on it) or, with acknowledged=false, clear the acknowledgement. While acknowledged, reminders pause; an escalation to a higher severity still notifies and clears it. Only open incidents; ids from list_alert_incidents.","inputSchema":{"type":"object","properties":{"incident_id":{"type":"string","description":"Alert incident ID: its UUID or its REST id (inc_\u2026)"},"acknowledged":{"type":"boolean","description":"false to unacknowledge. Default true."}},"required":["incident_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"status":{"type":"string"},"acknowledged":{"type":"boolean"},"acknowledgedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acknowledgedBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"changed":{"type":"boolean"}},"required":["id","ruleId","ruleName","status","acknowledged","acknowledgedAt","acknowledgedBy","changed"],"additionalProperties":true},"annotations":{"title":"Acknowledge Alert Incident","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_escalation_chains","title":"List Escalation Chains","description":"List escalation chains: ordered paging steps that notify destinations one step at a time until an incident is acknowledged or resolved. Shows each chain's steps, repeats and the rules using it. Attach one to a rule with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"destinationPublicId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","destinationPublicId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["chains","total"],"additionalProperties":true},"annotations":{"title":"List Escalation Chains","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"create_escalation_chain","title":"Create Escalation Chain","description":"Create an escalation chain: steps that each notify their destinations, then wait for an acknowledgement before the next step. A rule tier that names the chain still notifies its own destinations at once; the chain pages on top until the incident is acknowledged or resolved. Attach it with create_alert_rule / update_alert_rule escalation_chain_id.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Chain name, e.g. 'Checkout on-call'"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5 (default 0)"},"description":{"type":"string","description":"Free-form notes"}},"required":["name","steps"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"destinationPublicId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","destinationPublicId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Create Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}}, - {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"destinationPublicId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","destinationPublicId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, + {"name":"update_escalation_chain","title":"Update Escalation Chain","description":"Change an escalation chain's name, steps, repeats or description. Pass only what changes; steps replaces every step. Escalations already running finish on the steps they started with. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to change: its UUID or its REST id (esc_\u2026)"},"name":{"type":"string","description":"New name"},"steps":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"array","items":{"type":"object","properties":{"destination_ids":{"type":"array","items":{"type":"string"},"description":"Destinations this step notifies, from list_alert_destinations (UUID or dest_\u2026)"},"wait_minutes":{"type":"number","description":"Minutes to wait for an acknowledgement before the next step, 1 to 1440"}},"required":["destination_ids","wait_minutes"],"additionalProperties":false}},"description":"Replaces every step. Steps in order, each notifying its destinations then waiting for an ack before the next, e.g. [{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":10},{\"destination_ids\":[\"dest_\u2026\"],\"wait_minutes\":15}]. At most 10 steps of 10 destinations"},"repeat_count":{"type":"number","description":"Extra passes over the whole chain after the first, 0 to 5"},"description":{"type":"string","description":"New notes, or \"none\" to clear them"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"chain":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"steps":{"type":"array","items":{"type":"object","properties":{"targets":{"type":"array","items":{"type":"object","properties":{"destinationId":{"type":"string"},"destinationPublicId":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["destinationId","destinationPublicId","name","type"],"additionalProperties":true}},"waitMinutes":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]}},"required":["targets","waitMinutes"],"additionalProperties":true}},"repeatCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"ruleIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","description","steps","repeatCount","ruleIds","createdAt","updatedAt"],"additionalProperties":true}},"required":["chain"],"additionalProperties":true},"annotations":{"title":"Update Escalation Chain","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"delete_escalation_chain","title":"Delete Escalation Chain","description":"Delete an escalation chain. Refused while a rule tier still names it: detach it first with update_alert_rule escalation_chain_id=\"none\" and escalation_chain_tier naming the tier, so another tier's chain stays; a tier that pages only through the chain needs a destination before that (add_destination_ids for a one-tier rule or the warning tier; a critical tier via the rule's thresholds in the v2 API or dashboard). Escalations already running finish. Ids from list_escalation_chains.","inputSchema":{"type":"object","properties":{"chain_id":{"type":"string","description":"Escalation chain to delete: its UUID or its REST id (esc_\u2026)"}},"required":["chain_id"],"additionalProperties":false},"outputSchema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":true},"annotations":{"title":"Delete Escalation Chain","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}}, {"name":"describe_dashboard_schema","title":"Describe Dashboard Schema","description":"What a dashboard widget can be: panel types, the four data-source kinds, the unit vocabulary, aggregations and group-by tokens, the display config, and (section templates) the create_dashboard templates. Generated from the live schema; read it before authoring or editing widgets rather than working from a remembered example.","inputSchema":{"type":"object","properties":{"section":{"type":"string","enum":["panel_types","data_sources","units","queries","display","raw_sql","templates"],"description":"Omit for the index plus the panel-type table."}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"section":{"type":"string"},"markdown":{"type":"string"}},"required":["markdown"],"additionalProperties":true},"annotations":{"title":"Describe Dashboard Schema","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, {"name":"list_dashboards","title":"List Dashboards","description":"List all dashboards with widget counts and timestamps. Use get_dashboard to see full widget configuration.","inputSchema":{"type":"object","properties":{"search":{"type":"string","description":"Filter dashboards by name (case-insensitive contains)"}},"additionalProperties":false},"outputSchema":{"type":"object","properties":{"dashboards":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"widgetCount":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","publicId","name","widgetCount","createdAt","updatedAt"],"additionalProperties":true}},"total":{"anyOf":[{"type":"number"},{"type":"string","enum":["Infinity","-Infinity","NaN"]}]},"search":{"type":"string"}},"required":["dashboards","total"],"additionalProperties":true},"annotations":{"title":"List Dashboards","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}}, diff --git a/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts b/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts index 9423e8073c..de19414a12 100644 --- a/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts +++ b/apps/ai/src/mcp/tools/__tests__/escalation-chain-tools.test.ts @@ -283,6 +283,14 @@ describe("escalation chain tools", () => { ) expect(seen.updated).toEqual({ name: "Renamed" }) + // "none" clears the notes; any other text sets them. + const cleared: Seen = {} + await ok("update_escalation_chain", { chain_id: CHAIN_ID, description: "none" }, cleared) + expect(cleared.updated).toEqual({ description: null }) + const noted: Seen = {} + await ok("update_escalation_chain", { chain_id: CHAIN_ID, description: "Weekend coverage" }, noted) + expect(noted.updated).toEqual({ description: "Weekend coverage" }) + const nothing = await run("update_escalation_chain", { chain_id: CHAIN_ID }, {}) expect(invalidParameter(nothing)).toBe("steps") const missing = await run("update_escalation_chain", { chain_id: MISSING_ID, repeat_count: 0 }, {}) diff --git a/apps/ai/src/mcp/tools/update-escalation-chain.ts b/apps/ai/src/mcp/tools/update-escalation-chain.ts index 801e988672..a973fd9883 100644 --- a/apps/ai/src/mcp/tools/update-escalation-chain.ts +++ b/apps/ai/src/mcp/tools/update-escalation-chain.ts @@ -1,5 +1,5 @@ import { McpInvalidInputError, type McpToolRegistrar } from "./types" -import { Effect, Schema } from "effect" +import { Effect, Schema, String as Str } from "effect" import { EscalationChainUpdateRequest } from "@maple/domain/http" import { EscalationChainMutationOutput } from "@maple/domain/mcp-outputs" import { EscalationChainsService } from "@maple/backend/services/alerts/EscalationChainsService" @@ -31,7 +31,7 @@ export function registerUpdateEscalationChainTool(server: McpToolRegistrar) { name: P.optionalText("New name"), steps: P.optionalJson(StepsInput, `Replaces every step. ${stepsParameterDescription}`), repeat_count: P.optionalNumber("Extra passes over the whole chain after the first, 0 to 5"), - description: P.optionalText("New notes"), + description: P.optionalText('New notes, or "none" to clear them'), }), output: EscalationChainMutationOutput, hints: { readOnly: false, destructive: false, idempotent: true }, @@ -54,7 +54,15 @@ export function registerUpdateEscalationChainTool(server: McpToolRegistrar) { ...(params.name === undefined ? undefined : { name: params.name }), ...(steps === undefined ? undefined : { steps }), ...(params.repeat_count === undefined ? undefined : { repeatCount: params.repeat_count }), - ...(params.description === undefined ? undefined : { description: params.description }), + ...(params.description === undefined + ? undefined + : // "none" clears the notes, as escalation_chain_id does on the rule tools. + { + description: + Str.toLowerCase(Str.trim(params.description)) === "none" + ? null + : params.description, + }), }).pipe(Effect.mapError(invalidChainRequest)) const tenant = yield* CurrentMcpTenant const chains = yield* EscalationChainsService From 1352903dd4004aa523ba26dbc7b6c034429f5af7 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 19:16:46 +0100 Subject: [PATCH 28/29] feat(alerts): a silence holds an escalation's due step until it ends A due step first asks whether a silence covers the incident (one rule read, only when a step would go out) and defers under one, rechecking every minute. An incident that opens under a silence still starts its chain; step 1 waits. When the silence ends, the catch-up page re-keys the chain's due step to its own time, so a destination in both the tier and the step is paged once. --- .../alerts/AlertsService.escalation.test.ts | 112 +++++++++++++++++- .../src/services/alerts/AlertsService.ts | 53 ++++++++- .../services/alerts/alert-escalation-runs.ts | 28 ++++- 3 files changed, 190 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts index 61e75dd01e..c1f96df927 100644 --- a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts +++ b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts @@ -1,11 +1,13 @@ import { AlertDestinationInUseError, AlertRuleUpsertRequest, + AlertSilenceCreateRequest, AlertValidationError, EscalationChainCreateRequest, EscalationChainId, EscalationChainInUseError, EscalationChainUpdateRequest, + IsoDateTimeString, WebhookAlertDestinationConfig, type AlertDestinationId, type AlertThreshold, @@ -13,11 +15,12 @@ import { type UserId, } from "@maple/domain/http" import { afterEach, assert, describe, it } from "@effect/vitest" -import { Duration, Effect, Layer, Schema, Array as Arr, String as Str } from "effect" +import { Clock, Duration, Effect, Layer, Schema, Array as Arr, String as Str } from "effect" import { TestClock } from "effect/testing" import { cleanupTestDbs, createTestDb, executeSql, type TestDb } from "@maple/backend/platform/test-pglite" import { Database } from "@maple/backend/platform/DatabaseLive" import { AlertsService, type AlertsServiceApi } from "./AlertsService" +import { AlertSilencesService } from "./AlertSilencesService" import { applyEscalationPlan, planEscalationWork, selectEscalationWork } from "./alert-escalation-runs" import { EscalationChainsService } from "./EscalationChainsService" import { @@ -32,6 +35,7 @@ import { } from "./alerts-service-test-support" const decodeChainId = Schema.decodeUnknownSync(EscalationChainId) +const decodeIso = Schema.decodeUnknownSync(IsoDateTimeString) const trackedDbs: TestDb[] = [] afterEach(() => cleanupTestDbs(trackedDbs)) @@ -174,6 +178,24 @@ const setAck = (testDb: TestDb, orgId: OrgId, acknowledged: boolean) => ), ) +/** Silences the `checkout` service for an hour; ended early with `silences.end`. */ +const silenceCheckout = (orgId: OrgId, userId: UserId) => + Effect.gen(function* () { + const silences = yield* AlertSilencesService + const nowMs = yield* Clock.currentTimeMillis + const silence = yield* silences.create( + orgId, + userId, + adminRoles, + new AlertSilenceCreateRequest({ + reason: "checkout deploy", + matchers: { services: ["checkout"] }, + endsAt: decodeIso(new Date(nowMs + 60 * 60_000).toISOString()), + }), + ) + return { end: silences.end(orgId, userId, adminRoles, silence.id) } + }) + describe("escalation chains", () => { it.effect("pages the next step after the wait, once, and ends after the last step", () => { const testDb = createTestDb(trackedDbs) @@ -448,6 +470,94 @@ describe("escalation chains", () => { }).pipe(Effect.provide(layerFor(testDb, state, requests))) }) + it.effect("holds a step that falls due under a silence, and sends it once the silence ends", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId, userId } = yield* setup("org_esc_silenced_step") + yield* alerts.runSchedulerTick() + yield* tick(alerts) + assert.sameMembers(requests, ["trigger team", "trigger slack"]) + const silence = yield* silenceCheckout(orgId, userId) + // The pager step falls due at minute 10; the silence holds it. + yield* ticks(alerts, 15) + assert.lengthOf(requests, 2) + assert.strictEqual((yield* runRows(testDb, orgId))[0]?.status, "active") + + yield* silence.end + yield* tick(alerts) + assert.deepStrictEqual(requests.slice(2), ["trigger pager"]) + yield* ticks(alerts, 5) + assert.deepStrictEqual(requests.slice(2), ["trigger pager"]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + + it.effect( + "starts the chain of an incident that opens under a silence, paging step 1 once it ends", + () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const { alerts, orgId, userId } = yield* setup("org_esc_opened_silenced") + const silence = yield* silenceCheckout(orgId, userId) + yield* alerts.runSchedulerTick() + yield* ticks(alerts, 5) + assert.deepStrictEqual(requests, []) + assert.deepStrictEqual(yield* runRows(testDb, orgId), [ + { status: "active", steps_sent: 0, chain_name: "API escalation" }, + ]) + + yield* silence.end + yield* tick(alerts) + // The tier's held-back trigger and the chain's first step, each once. + assert.sameMembers(requests, ["trigger team", "trigger slack"]) + yield* ticks(alerts, 5) + assert.sameMembers(requests, ["trigger team", "trigger slack"]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }, + ) + + it.effect("pages a destination in both the tier and the held step once when the silence ends", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const alerts = yield* AlertsService + const chains = yield* EscalationChainsService + const orgId = asOrgId("org_esc_silenced_dedupe") + const userId = asUserId("user_esc_silenced_dedupe") + const team = yield* webhook(alerts, orgId, userId, "team") + const chain = yield* chains.create( + orgId, + userId, + adminRoles, + new EscalationChainCreateRequest({ + name: "Team first", + steps: [{ targets: [{ type: "destination", destinationId: team.id }], waitMinutes: 5 }], + }), + ) + yield* alerts.createRule( + orgId, + userId, + adminRoles, + ruleRequest([tier("critical", 5, [team.id], chain.id)]), + ) + const silence = yield* silenceCheckout(orgId, userId) + yield* alerts.runSchedulerTick() + yield* ticks(alerts, 3) + yield* silence.end + // Off the minute: the held step fell due before the catch-up page went out. + yield* TestClock.adjust(Duration.seconds(90)) + yield* alerts.runSchedulerTick() + assert.deepStrictEqual(requests, ["trigger team"]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + it.effect("restarts on the critical tier's own chain when a warning incident escalates", () => { const testDb = createTestDb(trackedDbs) const state = { tracesAggregateRows: errorRateRows(10) } diff --git a/packages/backend/src/services/alerts/AlertsService.ts b/packages/backend/src/services/alerts/AlertsService.ts index 21c1c9c028..d3084801f1 100644 --- a/packages/backend/src/services/alerts/AlertsService.ts +++ b/packages/backend/src/services/alerts/AlertsService.ts @@ -150,6 +150,7 @@ import { applyEscalationPlan, enabledDestinationIds, type EscalationWorkRow, + alignDueEscalationStep, endEscalationRunsAbove, loadEscalationChain, planEscalationWork, @@ -3708,6 +3709,13 @@ export class AlertsService extends Context.Service 0 && catchUpDeliveries.length > 0) yield* insertIncidentDeliveries(db, catchUpDeliveries) + // The chain's due step goes out keyed to the catch-up, so a destination in both is paged once. + if (caughtUp.length > 0) + yield* alignDueEscalationStep(db, { + orgId: row.orgId, + incidentId: openIncident.id, + atMs: timestamp, + }) const result = { updated, isCatchUpSent: caughtUp.length > 0 } if (updated.length === 0) return result // Back down a tier, the higher tier's chain stops paging its on-call. @@ -5809,6 +5817,41 @@ export class AlertsService extends Context.Service + db.run( + selectStoredAlertRules() + .where(($) => [$.orgId.eq(row.orgId), $.id.eq(row.ruleId)]) + .limit(1), + ), + ) + const ruleRow = ruleRows[0] + if (ruleRow === undefined) return null + const rule = yield* normalizeRuleRow(ruleRow).pipe( + Effect.asSome, + Effect.catchTag("@maple/http/errors/AlertRuleStoredConfigInvalidError", (error) => + Effect.logWarning( + "Escalation silence check skipped: the rule's config is invalid", + ).pipe( + Effect.annotateLogs({ + ruleId: row.ruleId, + runId: row.id, + error: error.message, + }), + Effect.as(Option.none()), + ), + ), + ) + if (Option.isNone(rule)) return null + return yield* findActiveSilence(row.orgId, rule.value, row.groupKey, nowMs) + }, + ) + /** One run: plan from the row, then a conditional write that only one tick can win. */ const advanceEscalationRun = Effect.fn("AlertsService.advanceEscalationRun")(function* ( row: EscalationWorkRow, @@ -5820,7 +5863,15 @@ export class AlertsService extends Context.Service + db + .run( + PG.update(AlertEscalationRuns) + .set({ nextStepAt: input.atMs, updatedAt: input.atMs }) + .where(($) => [ + $.orgId.eq(input.orgId), + $.incidentId.eq(input.incidentId), + $.status.eq("active"), + $.nextStepAt.lte(input.atMs + ESCALATION_SILENCE_RECHECK_MS), + ]), + ) + .pipe(Effect.asVoid) + /** * Live runs with something to decide: an active run that is due or whose incident closed, and a * paused run whose ack is gone or whose incident closed. A run paused on a standing ack is not From 2cd8b77949564bdbbc91ec01d631de0688eba236 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 6 Nov 2026 19:28:43 +0100 Subject: [PATCH 29/29] fix(alerts): a held step skips a destination the silence's renotify catch-up already paged Co-Authored-By: Claude Opus 5.5 --- .../alerts/AlertsService.escalation.test.ts | 47 +++++++++++++++++-- .../services/alerts/alert-escalation-runs.ts | 31 +++++++++++- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts index c1f96df927..79438f24b0 100644 --- a/packages/backend/src/services/alerts/AlertsService.escalation.test.ts +++ b/packages/backend/src/services/alerts/AlertsService.escalation.test.ts @@ -178,8 +178,8 @@ const setAck = (testDb: TestDb, orgId: OrgId, acknowledged: boolean) => ), ) -/** Silences the `checkout` service for an hour; ended early with `silences.end`. */ -const silenceCheckout = (orgId: OrgId, userId: UserId) => +/** Silences the `checkout` service, for an hour by default; ended early with `silences.end`. */ +const silenceCheckout = (orgId: OrgId, userId: UserId, minutes = 60) => Effect.gen(function* () { const silences = yield* AlertSilencesService const nowMs = yield* Clock.currentTimeMillis @@ -190,7 +190,7 @@ const silenceCheckout = (orgId: OrgId, userId: UserId) => new AlertSilenceCreateRequest({ reason: "checkout deploy", matchers: { services: ["checkout"] }, - endsAt: decodeIso(new Date(nowMs + 60 * 60_000).toISOString()), + endsAt: decodeIso(new Date(nowMs + minutes * 60_000).toISOString()), }), ) return { end: silences.end(orgId, userId, adminRoles, silence.id) } @@ -558,6 +558,47 @@ describe("escalation chains", () => { }).pipe(Effect.provide(layerFor(testDb, state, requests))) }) + it.effect("does not page a held step's destination again when the catch-up is a renotify", () => { + const testDb = createTestDb(trackedDbs) + const state = { tracesAggregateRows: errorRateRows(10) } + const requests: Array = [] + return Effect.gen(function* () { + yield* TestClock.setTime(DEFAULT_CLOCK_EPOCH_MS) + const alerts = yield* AlertsService + const chains = yield* EscalationChainsService + const orgId = asOrgId("org_esc_silenced_renotify") + const userId = asUserId("user_esc_silenced_renotify") + const team = yield* webhook(alerts, orgId, userId, "team") + const step = { + targets: [{ type: "destination" as const, destinationId: team.id }], + waitMinutes: 5, + } + const chain = yield* chains.create( + orgId, + userId, + adminRoles, + new EscalationChainCreateRequest({ name: "Team twice", steps: [step, step] }), + ) + yield* alerts.createRule( + orgId, + userId, + adminRoles, + ruleRequest([tier("critical", 5, [team.id], chain.id)]), + ) + yield* alerts.runSchedulerTick() + yield* tick(alerts) + assert.deepStrictEqual(requests, ["trigger team"]) + const silence = yield* silenceCheckout(orgId, userId, 120) + // Step 2 falls due at minute 5 and the hourly reminder at 60; the silence holds both. + yield* ticks(alerts, 62) + assert.deepStrictEqual(requests, ["trigger team"]) + yield* silence.end + yield* tick(alerts) + yield* ticks(alerts, 3) + assert.deepStrictEqual(requests, ["trigger team", "renotify team"]) + }).pipe(Effect.provide(layerFor(testDb, state, requests))) + }) + it.effect("restarts on the critical tier's own chain when a warning incident escalates", () => { const testDb = createTestDb(trackedDbs) const state = { tracesAggregateRows: errorRateRows(10) } diff --git a/packages/backend/src/services/alerts/alert-escalation-runs.ts b/packages/backend/src/services/alerts/alert-escalation-runs.ts index 41950823f0..0e645cadf6 100644 --- a/packages/backend/src/services/alerts/alert-escalation-runs.ts +++ b/packages/backend/src/services/alerts/alert-escalation-runs.ts @@ -359,13 +359,40 @@ export const applyEscalationPlan = (db: MapleDb, input: ApplyEscalationPlanInput passCount: row.repeatCount + 1, }, } - const destinationIds = Arr.dedupe( + const stepDestinationIds = Arr.dedupe( Arr.filter( Arr.map(plan.step.targets, (target) => target.destinationId), (id) => input.enabled.has(id), ), ) - if (destinationIds.length === 0) { + // A silence's catch-up at this step's due time may have gone out as a renotify, which the + // step's trigger key would not dedupe against; those destinations are already paged. + const renotified = + stepDestinationIds.length === 0 + ? [] + : yield* db.run( + PG.from(AlertDeliveryEvents) + .select("destinationId") + .where(($) => [ + $.orgId.eq(row.orgId), + $.incidentId.eq(row.incidentId), + $.deliveryKey.in_( + ...Arr.map(stepDestinationIds, (destinationId) => + makeAlertDeliveryKey( + row.incidentId, + destinationId, + "renotify", + row.nextStepAt, + ), + ), + ), + ]), + ) + const destinationIds = Arr.filter( + stepDestinationIds, + (id) => !Arr.some(renotified, (delivery) => delivery.destinationId === id), + ) + if (stepDestinationIds.length === 0) { yield* Effect.logWarning( "Escalation step has no enabled destination; nobody was notified", ).pipe(