# 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.