# Quickstart: 111 — Findings Workflow V2 + SLA

**Date**: 2026-02-24  
**Branch**: `111-findings-workflow-sla`

---

## Prerequisites

- Sail services running (`vendor/bin/sail up -d`)
- Database migrated to latest
- At least one workspace with at least one tenant
- Queue worker running for queued jobs (e.g., `vendor/bin/sail artisan queue:work`)

---

## Implementation Phases

### Phase 1: Data Layer (Findings v2 Columns + Badges + Settings)

**Goal:** Extend `findings` to v2 lifecycle fields and add SLA policy setting.

1. Migrations (add v2 columns + indexes; two-phase if enforcing NOT NULL after backfill)
2. Update `App\Models\Finding` constants for v2 statuses and severities
3. BADGE-001: extend Finding status badge mapping to include v2 statuses (and legacy `acknowledged` mapping)
4. Settings:
   - Add `findings.sla_days` to `SettingsRegistry`
   - Expose in Workspace Settings UI (JSON textarea), validate via registry rules

**Run:** `vendor/bin/sail artisan migrate && vendor/bin/sail artisan test --compact --filter=SettingsRegistry`

---

### Phase 2: Workflow + RBAC (Server-Side Enforcement + Filament Actions)

**Goal:** Enforce transition rules, write timestamps, and provide safe UI actions.

1. Capabilities:
   - Add new `TENANT_FINDINGS_*` constants
   - Keep `TENANT_FINDINGS_ACKNOWLEDGE` as triage alias (migration window)
   - Update `RoleCapabilityMap`
2. Policy/service layer:
   - Enforce allowed transitions server-side
   - Require reasons for resolve/close/risk accept
   - Audit log every mutation (before/after + reason fields)
3. Filament `FindingResource`:
   - Remove drift-only default filters
   - Default to Open statuses across all finding types
   - Add quick filters (Open/Overdue/High severity/My assigned)
   - Add row actions + bulk actions per spec (grouped under “More”)

**Run:** `vendor/bin/sail artisan test --compact --filter=FindingWorkflow`

---

### Phase 3: Generators (Lifecycle Fields + Drift Recurrence + Stale Resolve)

**Goal:** Ensure findings lifecycle fields and recurrence behavior are maintained automatically.

1. Drift:
   - Compute `recurrence_key` and upsert by it
   - Auto-reopen only from `resolved` into `reopened`
   - Auto-resolve stale drift for a scope when no longer detected (`resolved_reason=no_longer_detected`)
2. Permission posture + Entra roles:
   - Preserve existing reopen/auto-resolve behavior
   - Add lifecycle fields: first/last seen, times_seen, due_at/sla_days

**Run:** `vendor/bin/sail artisan test --compact --filter=FindingRecurrence`

---

### Phase 4: Alerts (SLA Due Producer + UI Re-Enable)

**Goal:** Make `sla_due` alert rules functional.

1. Add SLA due producer to `EvaluateAlertsJob` that emits one tenant-level event summarizing overdue counts.
2. Re-enable `sla_due` in AlertRuleResource event type options (only after producer exists).

**Manual verification:**
1. Create (or backfill) a finding with `due_at` in the past and open status.
2. Run `vendor/bin/sail artisan tenantpilot:alerts:dispatch --workspace={id}`
3. Confirm an `alert_deliveries` row is created for matching enabled rules.

**Run:** `vendor/bin/sail artisan test --compact --filter=FindingSlaDue`

---

### Phase 5: Backfill/Consolidation (OperationRun-Backed)

**Goal:** Upgrade legacy findings and consolidate drift duplicates.

1. Trigger backfill from tenant context (Filament action and/or an artisan command entrypoint).
2. Verify OPS-UX:
   - queued toast intent-only
   - progress visible in active ops widget + OperationRun detail
   - exactly one terminal completion notification (initiator-only)
3. Verify data outcomes:
   - `acknowledged → triaged`
   - lifecycle fields populated
   - due dates set from backfill time + SLA days for legacy open findings
   - drift duplicates consolidated (one canonical open row per recurrence identity)

**Run:** `vendor/bin/sail artisan test --compact --filter=FindingBackfill`

---

## Formatting / Hygiene

- Run `vendor/bin/sail bin pint --dirty` before finalizing.

