TenantAtlas/specs/111-findings-workflow-sla/tasks.md
ahmido 7ac53f4cc4 feat(111): findings workflow + SLA settings (#135)
Implements spec 111 (Findings workflow + SLA) and fixes Workspace findings SLA settings UX/validation.

Key changes:
- Findings workflow service + SLA policy and alerting.
- Workspace settings: allow partial SLA overrides without auto-filling unset severities in the UI; effective values still resolve via defaults.
- New migrations, jobs, command, UI/resource updates, and comprehensive test coverage.

Tests:
- `vendor/bin/sail artisan test --compact` (1779 passed, 8 skipped).

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #135
2026-02-25 01:48:01 +00:00

340 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
description: "Task list for Findings Workflow V2 + SLA (111)"
---
# Tasks: Findings Workflow V2 + SLA (111)
**Input**: Design documents from `specs/111-findings-workflow-sla/`
**Prerequisites**: `specs/111-findings-workflow-sla/plan.md` (required), `specs/111-findings-workflow-sla/spec.md` (required for user stories), `specs/111-findings-workflow-sla/research.md`, `specs/111-findings-workflow-sla/data-model.md`, `specs/111-findings-workflow-sla/contracts/api-contracts.md`, `specs/111-findings-workflow-sla/quickstart.md`
**Tests**: REQUIRED (Pest) — runtime behavior + UX contract enforcement.
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Confirm a clean baseline before large schema + workflow changes.
- [X] T001 [P] Start local environment (Sail) and confirm containers are healthy via `vendor/bin/sail`
- [X] T002 [P] Run baseline Pest suite for current Findings/Drift flows and record failures (if any) in `tests/Feature/Drift/`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Core data model + RBAC + settings + badges that MUST be complete before ANY user story can ship.
**⚠️ CRITICAL**: No user story work can begin until this phase is complete.
- [X] T003 Add v2 lifecycle/SLA/ownership columns to findings in `database/migrations/2026_02_24_160000_add_finding_lifecycle_v2_fields_to_findings_table.php`
- [X] T004 Add recurrence_key + SLA/query indexes for findings in `database/migrations/2026_02_24_160001_add_finding_recurrence_key_and_sla_indexes_to_findings_table.php`
- [X] T005 Update v2 Finding model (statuses, casts, relationships, open/terminal status helpers, legacy acknowledged mapping) in `app/Models/Finding.php`
- [X] T006 Update Finding status badge mapping for v2 statuses (incl legacy `acknowledged``triaged` surface) in `app/Support/Badges/Domains/FindingStatusBadge.php`
- [X] T007 [P] Update status badge unit tests for v2 mapping in `tests/Unit/Badges/FindingBadgesTest.php`
- [X] T008 [P] Update status badge feature tests for v2 mapping in `tests/Feature/Support/Badges/FindingBadgeTest.php`
- [X] T009 Update Finding factory defaults/states to populate new lifecycle fields for tests in `database/factories/FindingFactory.php`
- [X] T010 Add configurable Findings SLA policy setting (`findings.sla_days`) with defaults + validation + normalizer in `app/Support/Settings/SettingsRegistry.php`
- [X] T011 Expose Findings SLA policy setting in Workspace Settings UI in `app/Filament/Pages/Settings/WorkspaceSettings.php`
- [X] T012 [P] Add settings tests for findings SLA policy validation/normalization in `tests/Unit/Settings/FindingsSlaDaysSettingTest.php`
- [X] T013 Add v2 Findings capabilities (view/triage/assign/resolve/close/risk_accept) to registry in `app/Support/Auth/Capabilities.php`
- [X] T014 Map v2 Findings capabilities to tenant roles and keep `TENANT_FINDINGS_ACKNOWLEDGE` as deprecated alias for triage in `app/Services/Auth/RoleCapabilityMap.php`
- [X] T015 Update FindingPolicy to require `TENANT_FINDINGS_VIEW` and expose per-action authorization for workflow mutations in `app/Policies/FindingPolicy.php`
- [X] T016 Update review pack open-finding selection to use v2 open statuses helper in `app/Jobs/GenerateReviewPackJob.php`
- [X] T017 Update review pack fingerprint computation to use v2 open statuses helper in `app/Services/ReviewPackService.php`
**Checkpoint**: Foundation ready; user story implementation can now begin.
---
## Phase 3: User Story 1 - See Open Findings (Priority: P1) 🎯 MVP
**Goal**: Default Findings list shows open findings across all finding types (not drift-only), with quick filters and due/assignee visibility.
**Independent Test**: Seed a tenant with findings across multiple types and statuses, then verify the default list shows open workflow statuses across all types without adjusting filters.
### Tests for User Story 1
- [X] T018 [P] [US1] Add default list visibility test (open statuses across all types) in `tests/Feature/Findings/FindingsListDefaultsTest.php`
- [X] T019 [P] [US1] Add quick filter tests (Overdue, High severity, My assigned) in `tests/Feature/Findings/FindingsListFiltersTest.php`
### Implementation for User Story 1
- [X] T020 [US1] Update FindingResource access gating to use `TENANT_FINDINGS_VIEW` in `app/Filament/Resources/FindingResource.php`
- [X] T021 [US1] Remove drift-only + status=new default filters and default to open v2 statuses in `app/Filament/Resources/FindingResource.php`
- [X] T022 [US1] Add quick filters (Open/Overdue/High severity/My assigned) and due_at/assignee columns in `app/Filament/Resources/FindingResource.php`
- [X] T023 [US1] Update ListFindings filter helpers to match new filter shapes/defaults in `app/Filament/Resources/FindingResource/Pages/ListFindings.php`
**Checkpoint**: User Story 1 is functional and independently testable.
---
## Phase 4: User Story 2 - Triage, Assign, And Resolve (Priority: P1)
**Goal**: Consistent v2 workflow actions (single-record) with server-side enforcement, reasons, timestamps, audit logging, and correct 404/403 semantics.
**Independent Test**: Create an open finding, execute each allowed status transition, and verify transitions are enforced server-side, recorded with timestamps/actors, and audited.
### Tests for User Story 2
- [X] T024 [P] [US2] Add workflow service unit tests (transition validation, reasons required, due_at reset on reopen, due_at stability across severity changes while open, assignment targets must be current tenant members) in `tests/Unit/Findings/FindingWorkflowServiceTest.php`
- [X] T025 [P] [US2] Add Livewire row-action workflow tests (triage/start/assign/resolve/close/risk accept/reopen; assignee/owner picker limited to current tenant members; non-member IDs rejected) in `tests/Feature/Findings/FindingWorkflowRowActionsTest.php`
- [X] T026 [P] [US2] Add Livewire view-header workflow tests (same action set; assignee/owner picker limited to current tenant members) in `tests/Feature/Findings/FindingWorkflowViewActionsTest.php`
- [X] T027 [P] [US2] Add RBAC 404/403 matrix tests for workflow mutations (non-member 404; member missing cap 403) in `tests/Feature/Findings/FindingRbacTest.php`
- [X] T028 [P] [US2] Add audit log tests for workflow mutations (before/after + reasons + actor; assert evidence payloads are never included) in `tests/Feature/Findings/FindingAuditLogTest.php`
### Implementation for User Story 2
- [X] T029 [US2] Implement SLA resolver service (reads `findings.sla_days` via SettingsResolver) in `app/Services/Findings/FindingSlaPolicy.php`
- [X] T030 [US2] Implement workflow transition service (enforced map + timestamps + reason validation + due_at semantics + assignee/owner tenant membership validation + AuditLogger) in `app/Services/Findings/FindingWorkflowService.php`
- [X] T031 [US2] Update Finding model legacy methods/compat helpers for v2 workflow (keep legacy `acknowledged` readable) in `app/Models/Finding.php`
- [X] T032 [US2] Replace acknowledge row action with v2 workflow row actions (UiEnforcement, confirmations for destructive, assignee/owner options limited to current tenant members) in `app/Filament/Resources/FindingResource.php`
- [X] T033 [US2] Add v2 workflow actions to ViewFinding header actions (same capability gates + confirmations; assignee/owner options limited to current tenant members) in `app/Filament/Resources/FindingResource/Pages/ViewFinding.php`
- [X] T034 [US2] Expand ViewFinding infolist to show lifecycle + assignment + SLA fields (first/last seen, times_seen, due_at, assignee/owner, timestamps, reasons; preserve historical assignee/owner display even if membership is later removed) in `app/Filament/Resources/FindingResource.php`
- [X] T035 [US2] Update FindingResource ActionSurface declaration to satisfy v2 UI Action Matrix (detail header actions now present) in `app/Filament/Resources/FindingResource.php`
- [X] T036 [US2] Update legacy drift row-action test from acknowledge to triage in `tests/Feature/Drift/DriftAcknowledgeTest.php`
- [X] T037 [US2] Update legacy drift row-action authorization test from acknowledge to triage capability semantics in `tests/Feature/Drift/DriftAcknowledgeAuthorizationTest.php`
- [X] T038 [US2] Update Finding model behavior tests for v2 workflow semantics in `tests/Feature/Models/FindingResolvedTest.php`
**Checkpoint**: Single-record workflow is functional, enforced, and audited.
---
## Phase 5: User Story 3 - SLA Due Visibility And Alerts (Priority: P1)
**Goal**: SLA due alert producer emits one tenant-level event when newly-overdue open findings exist; AlertRule UI allows selecting “SLA due”.
**Independent Test**: Create newly-overdue open findings for a tenant, run alert evaluation, and verify a single tenant-level SLA due event is produced and can match an enabled alert rule.
### Tests for User Story 3
- [X] T039 [P] [US3] Add SLA due producer tests (tenant-level aggregation + newly overdue gating using `window_start`; payload includes overdue_total + overdue_by_severity; idempotent per tenant+window) in `tests/Feature/Alerts/SlaDueAlertTest.php`
- [X] T040 [P] [US3] Update test asserting sla_due is hidden to now assert it is selectable in `tests/Feature/ReviewPack/ReviewPackPruneTest.php`
- [X] T041 [P] [US3] Extend event type options test coverage to include sla_due label in `tests/Feature/EntraAdminRoles/AdminRolesAlertIntegrationTest.php`
### Implementation for User Story 3
- [X] T042 [US3] Implement `slaDueEvents()` producer and include in EvaluateAlertsJob event list in `app/Jobs/Alerts/EvaluateAlertsJob.php` (use `window_start` from alert evaluation; `fingerprint_key` stable per tenant+window; event metadata contains overdue_total + overdue_by_severity)
- [X] T043 [US3] Re-enable `sla_due` option in AlertRuleResource event type options in `app/Filament/Resources/AlertRuleResource.php`
**Checkpoint**: SLA due alerting is end-to-end functional.
---
## Phase 6: User Story 4 - Recurrence Reopens (Priority: P2)
**Goal**: Recurring findings reopen (from resolved only), lifecycle counters update, drift uses stable recurrence identity, and stale drift auto-resolves.
**Independent Test**: Simulate a finding being resolved and then being detected again, verifying it transitions to `reopened`, counters update, and due date resets.
### Tests for User Story 4
- [X] T044 [P] [US4] Add drift recurrence tests (resolved→reopened; closed/risk_accepted stays terminal; times_seen increments; due_at resets; concurrency: do not auto-reopen if `resolved_at` is after observation time) in `tests/Feature/Findings/FindingRecurrenceTest.php`
- [X] T045 [P] [US4] Add stale drift auto-resolve test (not detected in latest run → resolved_reason=no_longer_detected) in `tests/Feature/Findings/DriftStaleAutoResolveTest.php`
- [X] T046 [P] [US4] Update permission posture generator tests for reopened + lifecycle fields in `tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php`
- [X] T047 [P] [US4] Update Entra admin roles generator tests for reopened + lifecycle fields in `tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php`
- [X] T048 [P] [US4] Update alert evaluation tests to include status=reopened where appropriate in `tests/Feature/Alerts/PermissionMissingAlertTest.php`
- [X] T049 [P] [US4] Update alert evaluation tests to include status=reopened where appropriate in `tests/Feature/EntraAdminRoles/AdminRolesAlertIntegrationTest.php`
### Implementation for User Story 4
- [X] T050 [US4] Add recurrence_key computation helpers (dimension + subject identity) for drift findings in `app/Services/Drift/DriftFindingGenerator.php`
- [X] T051 [US4] Upsert drift findings by `(tenant_id, recurrence_key)` and set canonical `fingerprint = recurrence_key` in `app/Services/Drift/DriftFindingGenerator.php`
- [X] T052 [US4] Maintain lifecycle fields (first/last seen, times_seen) and set due_at on create/reset on reopen for drift findings in `app/Services/Drift/DriftFindingGenerator.php` (do not retroactively change due_at on severity changes)
- [X] T053 [US4] Implement drift auto-reopen only from resolved → reopened (closed/risk_accepted remain terminal; still update seen fields; do not reopen if `resolved_at` is after observation time) in `app/Services/Drift/DriftFindingGenerator.php`
- [X] T054 [US4] Implement stale drift auto-resolve for open drift findings not seen in run (resolved_reason=no_longer_detected) in `app/Services/Drift/DriftFindingGenerator.php`
- [X] T055 [US4] Update permission posture generator to set lifecycle fields + due_at semantics + reopened status handling in `app/Services/PermissionPosture/PermissionPostureFindingGenerator.php` (do not retroactively change due_at on severity changes)
- [X] T056 [US4] Update Entra admin roles generator to set lifecycle fields + due_at semantics + reopened status handling in `app/Services/EntraAdminRoles/EntraAdminRolesFindingGenerator.php` (do not retroactively change due_at on severity changes)
- [X] T057 [US4] Update EvaluateAlertsJob existing producers to include status `reopened` where “new/re-detected” should alert in `app/Jobs/Alerts/EvaluateAlertsJob.php`
**Checkpoint**: Recurrence behavior is correct and drift stops creating “new row per re-drift” noise.
---
## Phase 7: User Story 6 - Backfill Existing Findings (Priority: P2)
**Goal**: One-time backfill/consolidation operation upgrades legacy findings to v2 fields, maps acknowledged→triaged, sets due_at from backfill time, and consolidates drift duplicates.
**Independent Test**: Seed legacy findings (missing lifecycle fields, `acknowledged` status, drift duplicates), run the backfill operation, and verify fields are populated, statuses are mapped, and duplicates are consolidated.
### Tests for User Story 6
- [X] T058 [P] [US6] Add backfill tests (ack→triaged, lifecycle fields, due_at from backfill time, drift duplicate consolidation) in `tests/Feature/Findings/FindingBackfillTest.php`
### Implementation for User Story 6
- [X] T059 [US6] Register operation type `findings.lifecycle.backfill` in OperationCatalog (label + duration) in `app/Support/OperationCatalog.php`
- [X] T060 [US6] Add backfill artisan command entrypoint (OperationRun-backed; deduped; dispatches job) in `app/Console/Commands/TenantpilotBackfillFindingLifecycle.php`
- [X] T061 [US6] Implement backfill job skeleton (lock + chunking + summary_counts updates via OperationRunService) in `app/Jobs/BackfillFindingLifecycleJob.php`
- [X] T062 [US6] Implement backfill mapping (acknowledged→triaged; set first_seen_at/last_seen_at/times_seen; due_at from backfill time + SLA days for legacy open) in `app/Jobs/BackfillFindingLifecycleJob.php`
- [X] T063 [US6] Implement drift recurrence_key computation for legacy drift evidence in `app/Jobs/BackfillFindingLifecycleJob.php`
- [X] T064 [US6] Implement drift duplicate consolidation (canonical row; duplicates resolved_reason=consolidated_duplicate; clear recurrence_key) in `app/Jobs/BackfillFindingLifecycleJob.php`
- [X] T065 [US6] Add tenant-context Filament action to trigger backfill with Ops-UX queued toast + View run link in `app/Filament/Resources/FindingResource/Pages/ListFindings.php`
**Checkpoint**: Backfill operation is observable, safe, and upgrades legacy data correctly.
---
## Phase 8: User Story 5 - Bulk Manage Findings (Priority: P3)
**Goal**: Bulk workflow actions (triage/assign/resolve/close/risk accept) are safe, audited, and efficient for high volumes.
**Independent Test**: Select multiple findings and run each bulk action, verifying that all selected findings update consistently and each change is audited.
### Tests for User Story 5
- [X] T066 [P] [US5] Add bulk workflow action tests (bulk triage/assign/resolve/close/risk accept + audit per record; cover >=100 records for at least one bulk action) in `tests/Feature/Findings/FindingBulkActionsTest.php`
- [X] T067 [P] [US5] Update legacy bulk acknowledge selected test to bulk triage selected in `tests/Feature/Drift/DriftBulkAcknowledgeTest.php`
- [X] T068 [P] [US5] Update legacy “acknowledge all matching” test to “triage all matching” in `tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingTest.php`
- [X] T069 [P] [US5] Update legacy bulk authorization tests to new bulk action names/capabilities in `tests/Feature/Drift/DriftBulkAcknowledgeAuthorizationTest.php`
- [X] T070 [P] [US5] Update legacy “all matching requires typed confirmation >100” test to triage-all-matching in `tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingConfirmationTest.php`
### Implementation for User Story 5
- [X] T071 [US5] Replace bulk acknowledge actions with bulk workflow actions (UiEnforcement + confirmations + AuditLogger) in `app/Filament/Resources/FindingResource.php`
- [X] T072 [US5] Implement “Triage all matching” header action (typed confirm >100; audited; respects current filters) in `app/Filament/Resources/FindingResource/Pages/ListFindings.php`
**Checkpoint**: Bulk management is safe and supports high-volume operations.
---
## Phase 9: Polish & Cross-Cutting Concerns
**Purpose**: Repo hygiene and end-to-end validation.
- [X] T073 [P] Run Pint on touched files and fix formatting via `./vendor/bin/sail bin pint --dirty`
- [X] T074 Run full test suite via Sail and fix failures in `tests/`
- [X] T075 Run quickstart validation for feature 111 in `specs/111-findings-workflow-sla/quickstart.md`
- [X] T076 Add post-backfill hardening migration to enforce NOT NULL on finding seen fields (`first_seen_at`, `last_seen_at`, `times_seen`) in `database/migrations/2026_02_24_160002_enforce_not_null_on_finding_seen_fields.php`
- [X] T077 [P] Verify no new external API calls were introduced (Graph/HTTP) by scanning touched files for `GraphClientInterface` usage and direct HTTP clients (e.g., `Http::`, `Guzzle`) and confirming all Findings/Alerts logic remains DB-only
---
## Dependencies & Execution Order
### Phase Dependencies
- **Setup (Phase 1)**: No dependencies
- **Foundational (Phase 2)**: Depends on Setup completion; BLOCKS all user stories
- **User Stories (Phase 3+)**:
- US1/US2/US3 can proceed in parallel after Foundational
- US4 depends on Foundational (and should align with US2 status model)
- US6 depends on Foundational and should follow US4s recurrence_key definition for drift
- US5 depends on US2 (bulk actions reuse the workflow service + action builders)
- **Polish (Phase 9)**: Depends on all desired user stories being complete
### User Story Dependencies
- **US1 (P1)**: No dependencies besides Foundational
- **US2 (P1)**: No dependencies besides Foundational
- **US3 (P1)**: Depends on Foundational (due_at exists + open status semantics defined)
- **US4 (P2)**: Depends on Foundational (v2 statuses + lifecycle fields exist)
- **US6 (P2)**: Depends on Foundational; recommended after US4 so drift recurrence_key matches generator semantics
- **US5 (P3)**: Depends on US2 (workflow service + single-record semantics must be correct first)
### Within Each User Story
- Tests MUST be written and FAIL before implementation
- Core services/helpers before UI actions
- UI actions before bulk mutations
- Story complete before moving to next priority
---
## Parallel Examples
### Parallel Example: User Story 1
```bash
# Tests can run in parallel:
T018 # default list visibility test
T019 # quick filter tests
# Implementation can be split by file:
T020 # resource access gating
T023 # list filter helper adjustments
```
### Parallel Example: User Story 2
```bash
# Tests can run in parallel:
T024 # workflow service unit tests
T025 # row action tests
T026 # view header action tests
T027 # RBAC semantics tests
T028 # audit log tests
# Implementation can be split by file:
T029 # SLA resolver service
T033 # view infolist expansion
T036 # legacy drift test migration
```
### Parallel Example: User Story 3
```bash
# Tests can run in parallel:
T039 # SLA due producer tests
T040 # AlertRuleResource option visibility test update
T041 # alert options label assertions
```
### Parallel Example: User Story 4
```bash
# Generator test updates can run in parallel:
T046 # permission posture generator tests
T047 # entra admin roles generator tests
T048 # permission_missing alert evaluation tests
T049 # entra admin roles alert evaluation tests
```
### Parallel Example: User Story 6
```bash
# Backfill code and tests can be split:
T058 # backfill tests
T059 # OperationCatalog registration
T060 # artisan command entrypoint
```
### Parallel Example: User Story 5
```bash
# Bulk tests can run in parallel with legacy test migrations:
T066 # bulk actions tests
T067 # migrate bulk-selected test
T068 # migrate triage-all-matching test
T069 # migrate bulk auth tests
```
---
## Implementation Strategy
### MVP First (User Story 1 Only)
1. Complete Phase 1: Setup
2. Complete Phase 2: Foundational
3. Complete Phase 3: User Story 1
4. STOP and validate User Story 1 independently
### Incremental Delivery
1. Setup + Foundational
2. US1 → validate
3. US2 → validate
4. US3 → validate
5. US4 + US6 → validate
6. US5 → validate
---
## Notes
- `[P]` tasks = can run in parallel (different files, no dependencies)
- `[US#]` label maps tasks to user stories for traceability
- Destructive-like actions MUST use `->requiresConfirmation()` and be capability-gated (UiEnforcement)
- Operations MUST comply with Ops-UX 3-surface contract when OperationRun is used