---

description: "Task list for 099 Alerts v1 (Teams + Email)"

---

# Tasks: 099 — Alerts v1 (Teams + Email)

**Input**: Design documents from `/specs/099-alerts-v1-teams-email/`
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/

- Spec: `/specs/099-alerts-v1-teams-email/spec.md`
- Plan: `/specs/099-alerts-v1-teams-email/plan.md`
- Research: `/specs/099-alerts-v1-teams-email/research.md`
- Data model: `/specs/099-alerts-v1-teams-email/data-model.md`
- Contracts: `/specs/099-alerts-v1-teams-email/contracts/openapi.yaml`

**Tests**: REQUIRED (Pest) — this feature changes runtime behavior.
**Operations**: Jobs that perform queued delivery MUST create/update canonical `OperationRun` records and show “View run” via the Monitoring hub.
**RBAC**: Enforce 404 vs 403 semantics and use capability registry constants (no raw strings).

## Format: `[ID] [P?] [Story] Description`

---

## Phase 1: Setup (Shared Infrastructure)

**Purpose**: Basic structure required by the plan

- [X] T001 Create alerts namespace directories via keep-files `app/Services/Alerts/.gitkeep` and `app/Jobs/Alerts/.gitkeep`

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: MUST complete before any user story work begins

- [X] T002 Add Alerts capability constants to `app/Support/Auth/Capabilities.php` (`ALERTS_VIEW`, `ALERTS_MANAGE`)
- [X] T003 Update role → capability mapping in `app/Services/Auth/WorkspaceRoleCapabilityMap.php` for Alerts view/manage
- [X] T004 Add audit action IDs to `app/Support/Audit/AuditActionId.php` for Alerts (destination/rule create/update/delete + enable/disable)
- [X] T005 [P] Add operation type labels in `app/Support/OperationCatalog.php` (`alerts.evaluate`, `alerts.deliver`)
- [X] T006 [P] Implement workspace timezone resolver `app/Services/Alerts/WorkspaceTimezoneResolver.php` (fallback to `config('app.timezone')`)
- [X] T007 Update the Alerts “UI Action Matrix” in `specs/099-alerts-v1-teams-email/spec.md` for Targets/Rules/Deliveries surfaces (ensure it matches actual Filament actions)

**Checkpoint**: Capabilities + audit IDs + operation type labels exist.

---

## Phase 3: User Story 1 — Configure alert destinations (Priority: P1) 🎯 MVP

**Goal**: Workspace members with manage permission can create Teams/email destinations.

**Independent Test**: Create a Teams destination and an Email destination; ensure secrets are never revealed after save; list/edit/disable.

### Tests for User Story 1 ⚠️

- [X] T008 [P] [US1] Add RBAC access tests (404 vs 403) in `tests/Feature/Filament/Alerts/AlertDestinationAccessTest.php`
- [X] T009 [P] [US1] Add destination CRUD happy-path test in `tests/Feature/Filament/Alerts/AlertDestinationCrudTest.php`

### Implementation for User Story 1

- [X] T010 [P] [US1] Create migration `database/migrations/*_create_alert_destinations_table.php` (workspace_id, type, is_enabled, encrypted config, timestamps)
- [X] T011 [P] [US1] Create model `app/Models/AlertDestination.php` (workspace relationship; encrypted config cast; hide config)
- [X] T012 [P] [US1] Create policy `app/Policies/AlertDestinationPolicy.php` (404 non-member; 403 missing capability)
- [X] T013 [US1] Register policy mapping in `app/Providers/AuthServiceProvider.php`
- [X] T014 [US1] Implement Filament resource `app/Filament/Resources/AlertDestinationResource.php` (Targets; action-surface contract; max 2 visible row actions)
- [X] T015 [US1] Add Alerts navigation entry via Filament cluster (`app/Filament/Clusters/Monitoring/AlertsCluster.php`) and register Targets resource under it
- [X] T016 [US1] Add audited mutations in `app/Filament/Resources/AlertDestinationResource.php` using `app/Services/Audit/WorkspaceAuditLogger.php` + `app/Support/Audit/AuditActionId.php` (no secrets)
- [X] T017 [US1] Ensure destructive actions confirm in `app/Filament/Resources/AlertDestinationResource.php` (`->action(...)` + `->requiresConfirmation()`)

**Checkpoint**: Destinations are functional and testable independently.

---

## Phase 4: User Story 2 — Configure alert routing rules (Priority: P2)

**Goal**: Workspace members with manage permission can define routing rules that send to destination(s).

**Independent Test**: Create a rule, attach destinations, set tenant allowlist, and verify it is enforced during evaluation.

### Tests for User Story 2 ⚠️

- [X] T018 [P] [US2] Add RBAC access tests (404 vs 403) in `tests/Feature/Filament/Alerts/AlertRuleAccessTest.php`
- [X] T019 [P] [US2] Add rule CRUD test (destinations attach) in `tests/Feature/Filament/Alerts/AlertRuleCrudTest.php`

### Implementation for User Story 2

- [X] T020 [P] [US2] Create migrations `database/migrations/*_create_alert_rules_table.php` and `database/migrations/*_create_alert_rule_destinations_table.php`
- [X] T021 [P] [US2] Create models `app/Models/AlertRule.php` and `app/Models/AlertRuleDestination.php` (casts for allowlist + quiet-hours fields)
- [X] T022 [P] [US2] Create policy `app/Policies/AlertRulePolicy.php` (404 non-member; 403 missing capability)
- [X] T023 [US2] Register policy mapping in `app/Providers/AuthServiceProvider.php`
- [X] T024 [US2] Implement Filament resource `app/Filament/Resources/AlertRuleResource.php` (Rules; action-surface contract; destination picker)
- [X] T025 [US2] Implement enable/disable actions with audit logging in `app/Filament/Resources/AlertRuleResource.php` (use `->action(...)` and confirmations)
- [X] T026 [US2] Register Rules resource under Alerts cluster navigation (no manual `NavigationItem` duplicates)

**Checkpoint**: Rules are functional and testable independently.

---

## Phase 5: User Story 3 — Deliver alerts safely + review delivery history (Priority: P3)

**Goal**: Queue-driven delivery with fingerprint dedupe, cooldown suppression, quiet-hours deferral, and a delivery history viewer.

**Independent Test**: Trigger same event twice within cooldown → only one send + one `suppressed` record; quiet-hours → `deferred`; failures retry with exponential backoff then `failed`.

### Tests for User Story 3 ⚠️

- [X] T027 [P] [US3] Add delivery viewer access test in `tests/Feature/Filament/Alerts/AlertDeliveryViewerTest.php`
- [X] T028 [P] [US3] Add fingerprint/cooldown suppression test in `tests/Unit/Alerts/AlertSuppressionTest.php`
- [X] T029 [P] [US3] Add quiet-hours deferral test in `tests/Unit/Alerts/AlertQuietHoursTest.php`
- [X] T030 [P] [US3] Add retry/backoff terminal failure test in `tests/Unit/Alerts/AlertRetryPolicyTest.php`

### Implementation for User Story 3

- [X] T031 [P] [US3] Create deliveries migration `database/migrations/*_create_alert_deliveries_table.php` (workspace_id, tenant_id NOT NULL, status, fingerprint, send_after, attempt_count, last_error_code/message, indexes)
- [X] T032 [P] [US3] Create model `app/Models/AlertDelivery.php` (statuses incl. `suppressed`; prunable retention = 90 days default)
- [X] T033 [P] [US3] Create policy `app/Policies/AlertDeliveryPolicy.php` (view requires `ALERTS_VIEW`; enforce tenant entitlement; 404/403 semantics)
- [X] T034 [US3] Register policy mapping in `app/Providers/AuthServiceProvider.php`
- [X] T035 [P] [US3] Implement fingerprint + quiet-hours helpers `app/Services/Alerts/AlertFingerprintService.php` and `app/Services/Alerts/AlertQuietHoursService.php`
- [X] T036 [US3] Implement dispatcher `app/Services/Alerts/AlertDispatchService.php` (creates delivery rows; writes `suppressed` rows) with repo-grounded trigger sources:
  - High Drift: from `Finding` (drift) severity high/critical where `status=new` (unacknowledged); “newly active/visible” means first appearance (new finding created)
  - Compare Failed: from failed `OperationRun` where `type=drift_generate_findings`
  - SLA Due: safe no-op until a due-date signal exists in persistence
- [X] T037 [P] [US3] Implement Teams sender `app/Services/Alerts/TeamsWebhookSender.php` (Laravel HTTP client; no secret logging)
- [X] T038 [P] [US3] Implement Email notification `app/Notifications/Alerts/EmailAlertNotification.php` (no secrets)
- [X] T039 [P] [US3] Implement evaluate job `app/Jobs/Alerts/EvaluateAlertsJob.php` (creates deliveries; records `OperationRun` type `alerts.evaluate`)
- [X] T040 [P] [US3] Create dispatch command `app/Console/Commands/TenantpilotDispatchAlerts.php` (`tenantpilot:alerts:dispatch`) that queues evaluation + delivery work idempotently
- [X] T041 [P] [US3] Wire scheduler in `routes/console.php` to run `tenantpilot:alerts:dispatch` every minute with `->withoutOverlapping()`
- [X] T042 [P] [US3] Implement delivery job `app/Jobs/Alerts/DeliverAlertsJob.php` (sends due deliveries; bounded retries + exponential backoff; records `OperationRun` type `alerts.deliver`)
- [X] T043 [P] [US3] Implement send service `app/Services/Alerts/AlertSender.php` (shared send orchestration for Teams/email; safe error capture; no secret logging)
- [X] T044 [US3] Add deliveries Filament resource `app/Filament/Resources/AlertDeliveryResource.php` (read-only; inspection affordance; no secrets; list/query must not reveal deliveries for non-entitled tenants)
- [X] T045 [US3] Register Deliveries resource under Alerts cluster navigation (no manual `NavigationItem` duplicates)

**Checkpoint**: Delivery pipeline works with retries, suppression, quiet-hours deferral, and safe history.

---

## Phase 6: Polish & Cross-Cutting Concerns

- [X] T046 Update quickstart queue command in `specs/099-alerts-v1-teams-email/quickstart.md` (use `queue:work`)
- [X] T047 Run formatting `vendor/bin/sail bin pint --dirty` on `app/**` and `tests/**`
- [X] T048 Run focused tests `vendor/bin/sail artisan test --compact` for `tests/Feature/Filament/Alerts/**` and `tests/Unit/Alerts/**`

## Phase 7: Navigation UX (Monitoring)

- [X] T049 Restructure Alerts navigation under Monitoring (no Overview page; no content sub-navigation; Deliveries is default landing)
- [X] T050 Update OperateHubShell tests to use Alerts cluster landing and follow redirects

---

## Dependencies & Execution Order

### Phase Dependencies

- Setup (Phase 1) → Foundational (Phase 2) → User stories → Polish

### User Story Dependencies

- US1 → US2 → US3

---

## Parallel Execution Examples

### US1

- Tests: T008 + T009
- Model/migration/policy: T010 + T011 + T012

### US2

- Tests: T018 + T019
- Model/migrations/policy: T020 + T021 + T022

### US3

- Tests: T027–T030
- Building blocks: T031 + T032 + T035 + T037 + T038 + T039–T041

---

## Implementation Strategy

### MVP First (User Story 1 Only)

1. Complete Phase 1 + Phase 2
2. Complete US1
3. Validate via `tests/Feature/Filament/Alerts/AlertDestination*`

### Incremental Delivery

- Add US2 next, then US3; each story remains independently demoable.
