21 KiB
| 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
acknowledged→triagedsurface) inapp/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 inapp/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_ACKNOWLEDGEas deprecated alias for triage inapp/Services/Auth/RoleCapabilityMap.php - T015 Update FindingPolicy to require
TENANT_FINDINGS_VIEWand expose per-action authorization for workflow mutations inapp/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_VIEWinapp/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_daysvia SettingsResolver) inapp/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
acknowledgedreadable) inapp/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) intests/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 inapp/Jobs/Alerts/EvaluateAlertsJob.php(usewindow_startfrom alert evaluation;fingerprint_keystable per tenant+window; event metadata contains overdue_total + overdue_by_severity) - T043 [US3] Re-enable
sla_dueoption in AlertRuleResource event type options inapp/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_atis after observation time) intests/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 canonicalfingerprint = recurrence_keyinapp/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_atis after observation time) inapp/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
reopenedwhere “new/re-detected” should alert inapp/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.backfillin OperationCatalog (label + duration) inapp/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) indatabase/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
GraphClientInterfaceusage 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 US4’s 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)
- Complete Phase 1: Setup
- Complete Phase 2: Foundational
- Complete Phase 3: User Story 1
- STOP and validate User Story 1 independently
Incremental Delivery
- Setup + Foundational
- US1 → validate
- US2 → validate
- US3 → validate
- US4 + US6 → validate
- 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