TenantAtlas/specs/052-async-add-policies/tasks.md
2026-01-15 23:18:03 +01:00

5.5 KiB

description
Task list for implementing Async “Add Policies” to Backup Set (052)

Tasks: Async “Add Policies” to Backup Set (052)

Input: Design documents from /specs/052-async-add-policies/ Prerequisites: plan.md (required), spec.md (required)

Tests: Required (Pest), per spec.md (SC-001..SC-004).

Organization: Tasks are grouped by user story so each story can be implemented and tested independently.

Format: - [ ] T### [P?] [US#] Description (with file path)

  • [P]: Can run in parallel (different files, no dependencies)
  • [US#]: User story mapping (US1/US2/US3)

Path Conventions (Laravel)

  • App code: app/
  • DB: database/migrations/
  • Filament admin: app/Filament/Resources/
  • Livewire tables: app/Livewire/
  • Tests (Pest): tests/Feature/, tests/Unit/

Phase 1: Setup (Shared Infrastructure)

  • T001 [P] Confirm existing run infra supports this operation (BulkOperationRun statuses + unique idempotency index) and document mapping queued↔pending, partial↔completed_with_errors in specs/052-async-add-policies/plan.md
  • T002 [P] Decide and standardize taxonomy strings: operationType (backup_set.add_policies), resource (backup_set), action (add_policies) in specs/052-async-add-policies/spec.md and plan.md

Phase 2: User Story 1 - Add selected policies without blocking (Priority: P1) 🎯 MVP

Goal: “Add selected” returns quickly and queues background work with an observable Run record.

Independent Test: Trigger the action and assert a job is queued and a run exists; no Graph/capture work occurs in-request.

Tests (write first) ⚠️

  • T010 [P] [US1] Update/replace sync-path assertions in tests/Feature/Filament/BackupSetPolicyPickerTableTest.php to assert job dispatch + run creation
  • T011 [P] [US1] Add fail-hard guard test to ensure no Graph calls occur during the bulk action (mock App\\Services\\Graph\\GraphClientInterface and assert ->never())

Implementation

  • T020 [US1] Create queued job to add policies to a backup set in app/Jobs/AddPoliciesToBackupSetJob.php (uses run lifecycle + safe failures via BulkOperationService)
  • T021 [US1] Update bulk action handler in app/Livewire/BackupSetPolicyPickerTable.php to create/reuse BulkOperationRun and dispatch AddPoliciesToBackupSetJob (no inline snapshot capture)
  • T022 [US1] Emit “queued” notification with “View run” link on submit (Filament DB notification preferred) from app/Livewire/BackupSetPolicyPickerTable.php
  • T023 [US1] Emit completion/failure DB notification from app/Jobs/AddPoliciesToBackupSetJob.php with a safe summary

Checkpoint: US1 complete — submit is non-blocking, job executes, run is visible.


Phase 3: User Story 2 - Double click / repeated submissions are deduplicated (Priority: P2)

Goal: Matching queued/running operations reuse the same run and do not enqueue duplicates.

Independent Test: Call the action twice for the same tenant + backup set + selection and assert only one run and one job dispatch.

Tests ⚠️

  • T030 [P] [US2] Add idempotency test in tests/Feature/BackupSets/BackupSetAddPoliciesIdempotencyTest.php (or extend existing picker test) asserting one run + one queued job

Implementation

  • T031 [US2] Implement deterministic selection hashing and idempotency key creation using app/Support/RunIdempotency.php (sorted policy ids + option flags), and reuse active runs via findActiveBulkOperationRun
  • T032 [US2] Handle race conditions safely (unique index collisions) by recovering the existing run rather than failing the request
  • T033 [US2] Ensure the action is always async (no dispatchSync path for small selections) in app/Livewire/BackupSetPolicyPickerTable.php

Checkpoint: US2 complete — double clicks are safe and deduped.


Phase 4: User Story 3 - Failures are visible and safe (Priority: P3)

Goal: Failures are persisted safely and tenant isolation is enforced for run visibility.

Independent Test: Force failure paths and confirm safe failures persisted; cross-tenant access is forbidden.

Tests ⚠️

  • T040 [P] [US3] Add tenant isolation test for the created run (403 cross-tenant) in tests/Feature/BackupSets/BackupSetAddPoliciesTenantIsolationTest.php (or extend tests/Feature/RunAuthorizationTenantIsolationTest.php)
  • T041 [P] [US3] Add sanitization test: failure reason containing token-like content is stored as redacted (exercise BulkOperationService::sanitizeFailureReason)

Implementation

  • T042 [US3] Ensure job records per-item failures with sanitized reasons and does not store raw Graph payloads in run failures or notifications (app/Jobs/AddPoliciesToBackupSetJob.php)
  • T043 [US3] Record stable failure reason_code values (per spec.md) alongside sanitized short text in run failures (app/Jobs/AddPoliciesToBackupSetJob.php and/or app/Services/BulkOperationService.php)
  • T044 [US3] Record “already in backup set” as skipped (with reason_code already_in_backup_set) and ensure counts match spec.md (app/Jobs/AddPoliciesToBackupSetJob.php)
  • T045 [US3] Ensure job processes all items (no circuit breaker abort) and run status reflects partial completion (app/Jobs/AddPoliciesToBackupSetJob.php)

Checkpoint: US3 complete — failures are safe and observable; tenant isolation holds.


Phase 5: Polish & Validation

  • T050 [P] Run formatting on changed files with ./vendor/bin/pint --dirty
  • T051 Run targeted tests: ./vendor/bin/sail artisan test --filter=BackupSetAddPolicies