# Phase 1 — Data Model (099.1 Add-on) ## Entities ### AlertDestination (existing) - **Table**: `alert_destinations` - **Ownership**: workspace-owned - **Key fields (relevant here)**: - `id` - `workspace_id` - `type` (e.g. Teams webhook, Email) - `config` (Teams webhook URL or list of recipients) - `is_enabled` ### AlertDelivery (existing, extended for test sends) - **Table**: `alert_deliveries` - **Ownership**: - v1 deliveries for real alerts remain tenant-associated - **test deliveries for this add-on are tenantless** (workspace-only) - **Key fields (relevant here)**: - `id` - `workspace_id` (required) - `tenant_id` (**nullable for test deliveries**) - `alert_rule_id` (**nullable for test deliveries**) - `alert_destination_id` (required) - `event_type` (string, includes `alerts.test`) - `status` (`queued|deferred|sent|failed|suppressed|canceled`) - `send_after` (nullable; used for deferral/backoff) - `sent_at` (nullable) - `attempt_count` - `last_error_code`, `last_error_message` (sanitized) - `payload` (array/json) - timestamps: `created_at`, `updated_at` ## Relationships - `AlertDelivery` → `AlertDestination` (belongsTo via `alert_destination_id`) - `AlertDelivery` → `AlertRule` (belongsTo via `alert_rule_id`, nullable) - `AlertDelivery` → `Tenant` (belongsTo via `tenant_id`, nullable) ## New derived concepts (no storage) ### LastTestStatus (derived) Derived from the most recent `alert_deliveries` record where: - `alert_destination_id = {destination}` - `event_type = 'alerts.test'` Mapping: - no record → `Never` - `status in (queued, deferred)` → `Pending` - `status = sent` → `Sent` - `status = failed` → `Failed` Associated timestamp (derived): - Sent → `sent_at` - Failed → `updated_at` - Pending → `send_after` (fallback `created_at`) ## Validation / invariants - Creating a test delivery requires: - `alert_destination_id` exists and belongs to current workspace - destination is enabled (if disabled, refuse test request) - rate limit: no prior test delivery for this destination in last 60 seconds - Test delivery record must not persist secrets in payload or error message. ## Migration notes To support tenantless test deliveries: - Make `alert_deliveries.tenant_id` nullable and adjust the FK behavior. - Make `alert_deliveries.alert_rule_id` nullable and adjust the FK behavior. - Add or adjust indexes for efficient status lookup per destination + event type: - `(workspace_id, alert_destination_id, event_type, created_at)` (Exact migration steps and DB constraint changes are specified in the implementation plan.)