TenantAtlas/specs/111-findings-workflow-sla/tasks.md
2026-02-25 02:45:20 +01:00

21 KiB
Raw Blame History

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.

  • T001 [P] Start local environment (Sail) and confirm containers are healthy via vendor/bin/sail
  • 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.

  • 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
  • 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
  • T005 Update v2 Finding model (statuses, casts, relationships, open/terminal status helpers, legacy acknowledged mapping) in app/Models/Finding.php
  • T006 Update Finding status badge mapping for v2 statuses (incl legacy acknowledgedtriaged surface) in app/Support/Badges/Domains/FindingStatusBadge.php
  • T007 [P] Update status badge unit tests for v2 mapping in tests/Unit/Badges/FindingBadgesTest.php
  • T008 [P] Update status badge feature tests for v2 mapping in tests/Feature/Support/Badges/FindingBadgeTest.php
  • T009 Update Finding factory defaults/states to populate new lifecycle fields for tests in database/factories/FindingFactory.php
  • T010 Add configurable Findings SLA policy setting (findings.sla_days) with defaults + validation + normalizer in app/Support/Settings/SettingsRegistry.php
  • T011 Expose Findings SLA policy setting in Workspace Settings UI in app/Filament/Pages/Settings/WorkspaceSettings.php
  • T012 [P] Add settings tests for findings SLA policy validation/normalization in tests/Unit/Settings/FindingsSlaDaysSettingTest.php
  • T013 Add v2 Findings capabilities (view/triage/assign/resolve/close/risk_accept) to registry in app/Support/Auth/Capabilities.php
  • T014 Map v2 Findings capabilities to tenant roles and keep TENANT_FINDINGS_ACKNOWLEDGE as deprecated alias for triage in app/Services/Auth/RoleCapabilityMap.php
  • T015 Update FindingPolicy to require TENANT_FINDINGS_VIEW and expose per-action authorization for workflow mutations in app/Policies/FindingPolicy.php
  • T016 Update review pack open-finding selection to use v2 open statuses helper in app/Jobs/GenerateReviewPackJob.php
  • 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

  • T018 [P] [US1] Add default list visibility test (open statuses across all types) in tests/Feature/Findings/FindingsListDefaultsTest.php
  • T019 [P] [US1] Add quick filter tests (Overdue, High severity, My assigned) in tests/Feature/Findings/FindingsListFiltersTest.php

Implementation for User Story 1

  • T020 [US1] Update FindingResource access gating to use TENANT_FINDINGS_VIEW in app/Filament/Resources/FindingResource.php
  • T021 [US1] Remove drift-only + status=new default filters and default to open v2 statuses in app/Filament/Resources/FindingResource.php
  • T022 [US1] Add quick filters (Open/Overdue/High severity/My assigned) and due_at/assignee columns in app/Filament/Resources/FindingResource.php
  • 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

  • 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
  • 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
  • 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
  • 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
  • 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

  • T029 [US2] Implement SLA resolver service (reads findings.sla_days via SettingsResolver) in app/Services/Findings/FindingSlaPolicy.php
  • 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
  • T031 [US2] Update Finding model legacy methods/compat helpers for v2 workflow (keep legacy acknowledged readable) in app/Models/Finding.php
  • 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
  • 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
  • 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
  • T035 [US2] Update FindingResource ActionSurface declaration to satisfy v2 UI Action Matrix (detail header actions now present) in app/Filament/Resources/FindingResource.php
  • T036 [US2] Update legacy drift row-action test from acknowledge to triage in tests/Feature/Drift/DriftAcknowledgeTest.php
  • T037 [US2] Update legacy drift row-action authorization test from acknowledge to triage capability semantics in tests/Feature/Drift/DriftAcknowledgeAuthorizationTest.php
  • 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

  • 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
  • T040 [P] [US3] Update test asserting sla_due is hidden to now assert it is selectable in tests/Feature/ReviewPack/ReviewPackPruneTest.php
  • 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

  • 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)
  • 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

  • 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
  • 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
  • T046 [P] [US4] Update permission posture generator tests for reopened + lifecycle fields in tests/Feature/PermissionPosture/PermissionPostureFindingGeneratorTest.php
  • T047 [P] [US4] Update Entra admin roles generator tests for reopened + lifecycle fields in tests/Feature/EntraAdminRoles/EntraAdminRolesFindingGeneratorTest.php
  • T048 [P] [US4] Update alert evaluation tests to include status=reopened where appropriate in tests/Feature/Alerts/PermissionMissingAlertTest.php
  • T049 [P] [US4] Update alert evaluation tests to include status=reopened where appropriate in tests/Feature/EntraAdminRoles/AdminRolesAlertIntegrationTest.php

Implementation for User Story 4

  • T050 [US4] Add recurrence_key computation helpers (dimension + subject identity) for drift findings in app/Services/Drift/DriftFindingGenerator.php
  • T051 [US4] Upsert drift findings by (tenant_id, recurrence_key) and set canonical fingerprint = recurrence_key in app/Services/Drift/DriftFindingGenerator.php
  • 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)
  • 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
  • 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
  • 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)
  • 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)
  • 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

  • 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

  • T059 [US6] Register operation type findings.lifecycle.backfill in OperationCatalog (label + duration) in app/Support/OperationCatalog.php
  • T060 [US6] Add backfill artisan command entrypoint (OperationRun-backed; deduped; dispatches job) in app/Console/Commands/TenantpilotBackfillFindingLifecycle.php
  • T061 [US6] Implement backfill job skeleton (lock + chunking + summary_counts updates via OperationRunService) in app/Jobs/BackfillFindingLifecycleJob.php
  • 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
  • T063 [US6] Implement drift recurrence_key computation for legacy drift evidence in app/Jobs/BackfillFindingLifecycleJob.php
  • T064 [US6] Implement drift duplicate consolidation (canonical row; duplicates resolved_reason=consolidated_duplicate; clear recurrence_key) in app/Jobs/BackfillFindingLifecycleJob.php
  • 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

  • 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
  • T067 [P] [US5] Update legacy bulk acknowledge selected test to bulk triage selected in tests/Feature/Drift/DriftBulkAcknowledgeTest.php
  • T068 [P] [US5] Update legacy “acknowledge all matching” test to “triage all matching” in tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingTest.php
  • T069 [P] [US5] Update legacy bulk authorization tests to new bulk action names/capabilities in tests/Feature/Drift/DriftBulkAcknowledgeAuthorizationTest.php
  • 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

  • T071 [US5] Replace bulk acknowledge actions with bulk workflow actions (UiEnforcement + confirmations + AuditLogger) in app/Filament/Resources/FindingResource.php
  • 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.

  • T073 [P] Run Pint on touched files and fix formatting via ./vendor/bin/sail bin pint --dirty
  • T074 Run full test suite via Sail and fix failures in tests/
  • T075 Run quickstart validation for feature 111 in specs/111-findings-workflow-sla/quickstart.md
  • 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
  • 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

# 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

# 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

# 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

# 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

# Backfill code and tests can be split:
T058  # backfill tests
T059  # OperationCatalog registration
T060  # artisan command entrypoint

Parallel Example: User Story 5

# 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