# Implementation Plan: Findings Workflow V2 + SLA

**Branch**: `111-findings-workflow-sla` | **Date**: 2026-02-24 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/111-findings-workflow-sla/spec.md`  
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/111-findings-workflow-sla/spec.md`

**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.

## Summary

Standardize the Findings lifecycle across all finding types (drift, permission posture, Entra admin roles) by introducing a v2 workflow (`new → triaged → in_progress → resolved/closed/risk_accepted`, plus `reopened`), ownership/assignment, recurrence tracking, and due-date (SLA) behavior. Drift findings will stop creating “new row per re-drift” noise by using a stable recurrence identity and reopening the canonical record when a resolved issue reappears. SLA due alerting will be re-enabled by implementing a producer that emits a single tenant-level SLA due event (summarizing overdue counts) when newly-overdue open findings exist (at most one per tenant per evaluation window), and by re-adding the event type to AlertRule configuration once the producer exists. Review Pack “open findings” selection and fingerprinting will be updated to use the v2 open-status set. A one-time OperationRun-backed backfill/consolidation operation upgrades legacy findings (acknowledged → triaged, lifecycle fields populated, due dates assigned from backfill time + SLA days, drift duplicates consolidated).

## Technical Context

**Language/Version**: PHP 8.4.15 (Laravel 12.52.0)  
**Primary Dependencies**: Filament v5.2.1, Livewire v4.1.4  
**Storage**: PostgreSQL (JSONB used for evidence and settings values)  
**Testing**: Pest v4.3.1 (PHPUnit 12.5.4)  
**Target Platform**: Docker via Laravel Sail (local); Dokploy (staging/production)  
**Project Type**: Web application (Laravel monolith with Filament admin panel)  
**Performance Goals**: Findings list remains performant at 10k+ rows/tenant by relying on pagination and index-backed filters (status/severity/due date/assignee); bulk workflow actions handle 100 records per action; scheduled alert evaluation relies on index-backed queries over `(workspace_id, tenant_id, status, due_at)` and avoids full-table scans  
**Constraints**: No new external API calls; server-side enforcement for workflow transitions and RBAC; all long-running backfill/consolidation runs are OperationRun-backed with OPS-UX 3-surface feedback; Monitoring/Alerts evaluation remains DB-only at render time  
**Scale/Scope**: Multi-workspace, multi-tenant; findings volumes can grow continuously (recurrence reduces drift row churn); SLA due event is tenant-level (one per tenant per evaluation window)

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

| Rule | Status | Notes |
|------|--------|-------|
| **Inventory-first** | PASS | No change to inventory semantics. Findings remain operational artifacts derived from inventory-backed runs. |
| **Read/write separation** | PASS | Workflow mutations (triage/assign/resolve/close/risk accept/reopen) are explicit user actions with confirmation (where required), server-side authorization, audit logs, and tests. |
| **Graph contract path** | PASS | No new Graph calls. All changes are DB + UI + queued jobs. |
| **Deterministic capabilities** | PASS | New capabilities are added to the canonical registry (`app/Support/Auth/Capabilities.php`) and mapped in `RoleCapabilityMap`. Legacy `TENANT_FINDINGS_ACKNOWLEDGE` remains as a deprecated alias for v2 triage permission. |
| **RBAC-UX: two planes** | PASS | Only `/admin` plane involved. Tenant-context findings remain under `/admin/t/{tenant}/...`. |
| **RBAC-UX: non-member = 404** | PASS | UI uses `UiEnforcement`; server-side policies/guards return deny-as-not-found for non-members. |
| **RBAC-UX: member missing capability = 403** | PASS | UI disables with tooltips; server-side policies/guards deny with 403 for members missing capability. |
| **RBAC-UX: destructive confirmation** | PASS | Resolve/Close/Risk accept/Reopen require confirmation; bulk actions use confirmation and typed confirmation when large. |
| **RBAC-UX: global search** | N/A | Findings are tenant-context and already have a View page; no new global-search surfaces are introduced in this feature. |
| **Workspace isolation** | PASS | Findings queries remain workspace+tenant safe; overdue evaluation operates on findings scoped by `workspace_id` and `tenant_id`. |
| **Tenant isolation** | PASS | All finding reads/writes are tenant-scoped; bulk actions and backfill are tenant-context operations. |
| **Run observability** | PASS | Backfill/consolidation is OperationRun-backed. Alerts evaluation already uses OperationRun. |
| **Ops-UX 3-surface feedback** | PASS | Backfill uses `OperationUxPresenter` queued/dedupe toasts, standard progress surfaces, and `OperationRunCompleted` terminal DB notification (initiator-only). |
| **Ops-UX lifecycle/service-owned** | PASS | Any new run transitions use `OperationRunService` (no direct status/outcome writes). |
| **Ops-UX summary counts contract** | PASS | Backfill and generator updates use only keys allowed by `OperationSummaryKeys::all()` and numeric-only values. |
| **Ops-UX system runs** | PASS | Scheduled alert evaluation remains system-run (no initiator DB notification). Any tenant-wide escalation uses Alerts, not OperationRun notifications. |
| **Automation / idempotency** | PASS | Backfill runs are deduped per tenant + scope identity; queued jobs are idempotent and lock-protected where needed. |
| **Data minimization** | PASS | Evidence remains sanitized; audit logs store before/after at a metadata level and avoid secrets/tokens. |
| **BADGE-001** | PASS | Finding status badge mapping is extended to include all v2 statuses with tests. |
| **Filament Action Surface Contract** | PASS | Findings Resource already declares ActionSurface; will be updated to cover v2 actions with “More” grouping + bulk groups and empty-state exemption retained. |
| **UX-001** | PASS | Findings View already uses Infolist. Workflow actions are surfaced via header/row/bulk actions with safety and grouping conventions. |

**Post-design re-evaluation**: All checks PASS. No constitution violations expected after Phase 1 outputs below.

## Project Structure

### Documentation (this feature)

```text
/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/111-findings-workflow-sla/
├── plan.md                  # This file (/speckit.plan)
├── spec.md                  # Feature spec (/speckit.specify + /speckit.clarify)
├── checklists/
│   └── requirements.md      # Spec quality checklist
├── research.md              # Phase 0 output (/speckit.plan)
├── data-model.md            # Phase 1 output (/speckit.plan)
├── quickstart.md            # Phase 1 output (/speckit.plan)
├── contracts/               # Phase 1 output (/speckit.plan)
│   └── api-contracts.md
└── tasks.md                 # Phase 2 output (/speckit.tasks - NOT created by /speckit.plan)
```

### Source Code (repository root)

```text
app/
├── Console/Commands/
│   └── TenantpilotBackfillFindingLifecycle.php    # New: OperationRun-backed backfill entrypoint
├── Filament/Pages/Settings/
│   └── WorkspaceSettings.php                      # Updated: Findings SLA policy setting UI
├── Filament/Resources/
│   ├── FindingResource.php                        # Updated: defaults + actions + filters + workflow actions
│   └── AlertRuleResource.php                      # Updated: re-enable sla_due event type (after producer exists)
├── Jobs/
│   ├── BackfillFindingLifecycleJob.php            # New: queued worker for backfill + consolidation
│   └── Alerts/EvaluateAlertsJob.php               # Updated: add SLA due producer
├── Models/Finding.php                             # Updated: v2 statuses + workflow helpers + relationships
├── Policies/FindingPolicy.php                     # Updated: per-action capabilities
├── Services/
│   ├── Findings/
│   │   ├── FindingSlaPolicy.php                   # New: SLA policy resolver
│   │   └── FindingWorkflowService.php             # New: workflow transitions + audit
│   ├── Drift/DriftFindingGenerator.php            # Updated: recurrence_key + lifecycle fields + auto-resolve stale
│   ├── PermissionPosture/PermissionPostureFindingGenerator.php  # Updated: lifecycle fields + due_at semantics
│   └── EntraAdminRoles/EntraAdminRolesFindingGenerator.php      # Updated: lifecycle fields + due_at semantics
└── Support/
    ├── Auth/Capabilities.php                      # Updated: new tenant findings capabilities
    ├── Badges/Domains/FindingStatusBadge.php      # Updated: v2 status mapping
    ├── OperationCatalog.php                       # Updated: label + expected duration for backfill run type
    ├── OpsUx/OperationSummaryKeys.php             # Possibly updated if new summary keys are required
    └── Settings/SettingsRegistry.php              # Updated: findings.sla_days registry + validation

database/migrations/
├── 2026_02_24_160000_add_finding_lifecycle_v2_fields_to_findings_table.php
├── 2026_02_24_160001_add_finding_recurrence_key_and_sla_indexes_to_findings_table.php
└── 2026_02_24_160002_enforce_not_null_on_finding_seen_fields.php

tests/
├── Feature/Findings/
│   ├── FindingsListDefaultsTest.php               # Default list + open statuses across all types
│   ├── FindingsListFiltersTest.php                # Quick filters (Open/Overdue/High severity/My assigned)
│   ├── FindingWorkflowRowActionsTest.php          # Row workflow actions + confirmations
│   ├── FindingWorkflowViewActionsTest.php         # View header workflow actions
│   ├── FindingRbacTest.php                        # 404/403 matrix per capability
│   ├── FindingAuditLogTest.php                    # Audit before/after + reasons + actor
│   ├── FindingRecurrenceTest.php                  # drift recurrence_key + reopen + concurrency gating
│   ├── DriftStaleAutoResolveTest.php              # drift stale auto-resolve reason
│   ├── FindingBackfillTest.php                    # backfill + consolidation behavior
│   └── FindingBulkActionsTest.php                 # bulk actions + audit
├── Feature/Alerts/
│   └── SlaDueAlertTest.php                        # tenant-level sla_due event producer + rule selection
└── Unit/
    ├── Findings/FindingWorkflowServiceTest.php    # transition enforcement + due_at semantics
    └── Settings/FindingsSlaDaysSettingTest.php    # validation + normalization
```

**Structure Decision**: Standard Laravel monolith. Changes are concentrated in the Findings model/generators, Alerts evaluation, Filament resources, migrations, and tests.

## Complexity Tracking

> **Fill ONLY if Constitution Check has violations that must be justified**

| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| None | N/A | N/A |

## Filament v5 Agent Output Contract

1. **Livewire v4.0+ compliance**: Yes (Filament v5 requires Livewire v4; project is on Livewire v4.1.4).
2. **Provider registration**: No new panel required. Existing panel providers remain registered under `bootstrap/providers.php`.
3. **Global search**: FindingResource already has a View page. No new globally-searchable resources are introduced by this feature.
4. **Destructive actions**: Resolve/Close/Risk accept/Reopen and bulk equivalents use `->action(...)` + `->requiresConfirmation()` and server-side authorization.
5. **Asset strategy**: No new frontend asset pipeline requirements; standard Filament components only.
6. **Testing plan**: Pest coverage for workflow transitions, RBAC 404/403 semantics, drift recurrence_key behavior (reopen + stale auto-resolve), SLA due event producer, and backfill/consolidation.

## Phase 0 — Outline & Research (output: research.md)

Phase 0 resolves the key design decisions needed for implementation consistency:
- v2 status model + timestamp semantics
- SLA policy storage and defaults
- drift recurrence_key strategy (stable identity)
- SLA due event contract (tenant-level, throttling-friendly)
- backfill/consolidation approach (OperationRun-backed, idempotent)

Outputs:
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/111-findings-workflow-sla/research.md`

## Phase 1 — Design & Contracts (outputs: data-model.md, contracts/*, quickstart.md)

Design deliverables:
- Data model changes to `findings` and supporting settings/capabilities/badges
- Contracts for workflow actions and SLA due events (alerts)
- Implementation quickstart to validate locally with Sail

Outputs:
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/111-findings-workflow-sla/data-model.md`
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/111-findings-workflow-sla/contracts/api-contracts.md`
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/111-findings-workflow-sla/quickstart.md`

## Phase 2 — Planning (implementation outline; detailed tasks live in tasks.md)

- Migrations: add lifecycle + workflow columns + recurrence_key + indexes (two-phase: nullable → backfill → enforce not-null where appropriate).
- Workflow enforcement: server-side transition validation + timestamps + reasons; audit logs for every user-initiated workflow mutation.
- SLA policy: add `findings.sla_days` to SettingsRegistry + Workspace Settings UI; due_at set on create + reset on reopen.
- Generator updates:
  - Drift: stable recurrence_key upsert + reopen semantics + stale auto-resolve reason.
  - Permission posture + Entra roles: lifecycle fields + due_at semantics, keep reopen/auto-resolve behavior.
- Alerts: implement SLA due producer in EvaluateAlertsJob; re-enable `sla_due` option in AlertRuleResource event types.
- Filament UI: remove drift-only default filters; default to Open across all types; quick filters: Open, Overdue, High severity, My assigned; row + bulk actions for v2 workflow and assignment.
- Backfill operation: tenant-scoped backfill entrypoint and job with OperationRun observability + dedupe; consolidate drift duplicates into a canonical recurrence_key record and mark old duplicates terminal.
- Tests: workflow transition matrix + RBAC 404/403 behavior; recurrence + stale resolve; SLA due alert producer contract; backfill/consolidation correctness and idempotency.
