# Tasks: Feature 005 - Bulk Operations **Branch**: `feat/005-bulk-operations` | **Date**: 2025-12-22 **Input**: [spec.md](./spec.md), [plan.md](./plan.md), [data-model.md](./data-model.md), [research.md](./research.md) ## Task Format - **Checkbox**: `- [ ]` for incomplete, `- [x]` for complete - **Task ID**: Sequential T001, T002, T003... - **[P] marker**: Task can run in parallel (different files, no blocking dependencies) - **[Story] label**: User story tag (US1, US2, US3...) - omit for Setup/Foundational/Polish phases - **File path**: Always include exact file path in description ## Phase 1: Setup (Project Initialization) **Purpose**: Database schema and base infrastructure for bulk operations - [ ] T001 Create migration for `bulk_operation_runs` table in database/migrations/YYYY_MM_DD_HHMMSS_create_bulk_operation_runs_table.php - [ ] T002 Create migration to add `ignored_at` column to policies table in database/migrations/YYYY_MM_DD_HHMMSS_add_ignored_at_to_policies_table.php - [ ] T003 [P] Create BulkOperationRun model in app/Models/BulkOperationRun.php - [ ] T004 [P] Create BulkOperationService in app/Services/BulkOperationService.php - [ ] T005 Run migrations and verify schema: `./vendor/bin/sail artisan migrate` - [ ] T006 Run Pint formatting: `./vendor/bin/sail composer pint` **Checkpoint**: Database ready, base models created --- ## Phase 2: Foundational (Shared Components) **Purpose**: Core components used by ALL bulk operations - MUST complete before user stories **⚠️ CRITICAL**: No user story work can begin until this phase is complete - [ ] T007 Extend Policy model with `ignored_at` scope and methods in app/Models/Policy.php - [ ] T008 [P] Extend PolicyVersion model with `pruneEligible()` scope in app/Models/PolicyVersion.php - [ ] T009 [P] Extend RestoreRun model with `deletable()` scope in app/Models/RestoreRun.php - [ ] T010 Extend AuditLogger service to support bulk operation events in app/Services/Audit/AuditLogger.php (or equivalent) - [ ] T011 Update SyncPoliciesJob to filter by `ignored_at IS NULL` in app/Jobs/SyncPoliciesJob.php - [ ] T012 Create BulkOperationRun factory in database/factories/BulkOperationRunFactory.php - [ ] T013 Create test seeder BulkOperationsTestSeeder in database/seeders/BulkOperationsTestSeeder.php **Checkpoint**: Foundation ready - user story implementation can now begin in parallel --- ## Phase 3: User Story 1 - Bulk Delete Policies (Priority: P1) 🎯 MVP **Goal**: Enable admins to soft-delete multiple policies locally with `ignored_at` flag, preventing re-sync **Independent Test**: Select 15 policies → bulk delete → verify `ignored_at` set, policies hidden from listings, audit log created, Intune unchanged ### Tests for User Story 1 - [ ] T014 [P] [US1] Write unit test for BulkPolicyDeleteJob in tests/Unit/BulkPolicyDeleteJobTest.php - [ ] T015 [P] [US1] Write feature test for bulk delete <20 items (sync) in tests/Feature/BulkDeletePoliciesTest.php - [ ] T016 [P] [US1] Write feature test for bulk delete ≥20 items (async) in tests/Feature/BulkDeletePoliciesAsyncTest.php - [ ] T017 [P] [US1] Write permission test for bulk delete in tests/Unit/BulkActionPermissionTest.php ### Implementation for User Story 1 - [ ] T018 [P] [US1] Create BulkPolicyDeleteJob in app/Jobs/BulkPolicyDeleteJob.php - [ ] T019 [US1] Add bulk delete action to PolicyResource in app/Filament/Resources/PolicyResource.php - [ ] T020 [US1] Implement type-to-confirm modal (≥20 items) in PolicyResource bulk action - [ ] T021 [US1] Wire BulkOperationService to create tracking record and dispatch job - [ ] T022 [US1] Test bulk delete with 10 policies (sync, manual QA) - [ ] T023 [US1] Test bulk delete with 25 policies (async, manual QA) - [ ] T024 [US1] Run tests: `./vendor/bin/sail artisan test tests/Feature/BulkDeletePoliciesTest.php` - [ ] T025 [US1] Verify audit log entry created with correct metadata **Checkpoint**: Bulk delete policies working (sync + async), audit logged, tests passing --- ## Phase 4: User Story 2 - Bulk Export Policies to Backup (Priority: P1) **Goal**: Enable admins to export multiple policies to a new Backup Set with progress tracking **Independent Test**: Select 25 policies → export to backup → verify BackupSet created, 25 BackupItems exist, progress notification shown ### Tests for User Story 2 - [ ] T026 [P] [US2] Write unit test for BulkPolicyExportJob in tests/Unit/BulkPolicyExportJobTest.php - [ ] T027 [P] [US2] Write feature test for bulk export in tests/Feature/BulkExportToBackupTest.php - [ ] T028 [P] [US2] Write test for export with failures (3/25 fail) in tests/Feature/BulkExportFailuresTest.php ### Implementation for User Story 2 - [ ] T029 [P] [US2] Create BulkPolicyExportJob in app/Jobs/BulkPolicyExportJob.php - [ ] T030 [US2] Add bulk export action to PolicyResource in app/Filament/Resources/PolicyResource.php - [ ] T031 [US2] Create export form with backup_name and include_assignments fields - [ ] T032 [US2] Implement export logic (create BackupSet, capture BackupItems) - [ ] T033 [US2] Handle partial failures (some policies fail to backup) - [ ] T034 [US2] Test export with 30 policies (manual QA) - [ ] T035 [US2] Run tests: `./vendor/bin/sail artisan test tests/Feature/BulkExportToBackupTest.php` **Checkpoint**: Bulk export working, BackupSets created, failures handled gracefully --- ## Phase 5: User Story 5 - Type-to-Confirm (Priority: P1) **Goal**: Require typing "DELETE" for destructive operations with ≥20 items **Independent Test**: Bulk delete 25 policies → modal requires "DELETE" → button disabled until correct input → operation proceeds **Note**: This is implemented within US1 (T020) but tested separately here ### Tests for User Story 5 - [ ] T036 [P] [US5] Write test for type-to-confirm with correct input in tests/Feature/BulkTypeToConfirmTest.php - [ ] T037 [P] [US5] Write test for type-to-confirm with incorrect input (lowercase "delete") - [ ] T038 [P] [US5] Write test for type-to-confirm disabled for <20 items ### Validation for User Story 5 - [ ] T039 [US5] Manual test: Bulk delete 10 policies → confirm without typing (should work) - [ ] T040 [US5] Manual test: Bulk delete 25 policies → type "delete" → button stays disabled - [ ] T041 [US5] Manual test: Bulk delete 25 policies → type "DELETE" → button enables, operation proceeds - [ ] T042 [US5] Run tests: `./vendor/bin/sail artisan test tests/Feature/BulkTypeToConfirmTest.php` **Checkpoint**: Type-to-confirm working correctly for all thresholds --- ## Phase 6: User Story 6 - Progress Tracking (Priority: P2) **Goal**: Show real-time progress for queued bulk operations with Livewire polling **Independent Test**: Bulk delete 100 policies → progress notification updates every 5s → final notification shows outcomes ### Tests for User Story 6 - [ ] T043 [P] [US6] Write test for progress updates in BulkOperationRun in tests/Unit/BulkOperationRunProgressTest.php - [ ] T044 [P] [US6] Write feature test for progress notification in tests/Feature/BulkProgressNotificationTest.php - [ ] T045 [P] [US6] Write test for circuit breaker (abort >50% fail) in tests/Unit/CircuitBreakerTest.php ### Implementation for User Story 6 - [ ] T046 [P] [US6] Create Livewire progress component in app/Livewire/BulkOperationProgress.php - [ ] T047 [P] [US6] Create progress view in resources/views/livewire/bulk-operation-progress.blade.php - [ ] T048 [US6] Update BulkPolicyDeleteJob to emit progress after each chunk - [ ] T049 [US6] Update BulkPolicyExportJob to emit progress after each chunk - [ ] T050 [US6] Implement circuit breaker logic (abort if >50% fail) in jobs - [ ] T051 [US6] Add progress polling to Filament notifications or sidebar widget - [ ] T052 [US6] Test progress with 100 policies (manual QA, observe updates) - [ ] T053 [US6] Test circuit breaker with mock failures (manual QA) - [ ] T054 [US6] Run tests: `./vendor/bin/sail artisan test tests/Feature/BulkProgressNotificationTest.php` **Checkpoint**: Progress tracking working, polling updates UI, circuit breaker aborts high-failure jobs --- ## Phase 7: User Story 3 - Bulk Delete Policy Versions (Priority: P2) **Goal**: Enable admins to prune old policy versions that are NOT referenced and meet retention threshold (>90 days) **Independent Test**: Select 30 old versions → bulk prune → verify eligible deleted, ineligible skipped with reasons ### Tests for User Story 3 - [ ] T055 [P] [US3] Write unit test for pruneEligible() scope in tests/Unit/PolicyVersionEligibilityTest.php - [ ] T056 [P] [US3] Write unit test for BulkPolicyVersionPruneJob in tests/Unit/BulkPolicyVersionPruneJobTest.php - [ ] T057 [P] [US3] Write feature test for bulk prune in tests/Feature/BulkPruneVersionsTest.php - [ ] T058 [P] [US3] Write test for skip reasons (referenced, too recent, current) in tests/Feature/BulkPruneSkipReasonsTest.php ### Implementation for User Story 3 - [ ] T059 [P] [US3] Create BulkPolicyVersionPruneJob in app/Jobs/BulkPolicyVersionPruneJob.php - [ ] T060 [US3] Add bulk delete action to PolicyVersionResource in app/Filament/Resources/PolicyVersionResource.php - [ ] T061 [US3] Implement eligibility check per version in job (reuse pruneEligible scope) - [ ] T062 [US3] Collect skip reasons for ineligible versions - [ ] T063 [US3] Add type-to-confirm for ≥20 versions - [ ] T064 [US3] Test prune with 30 versions (15 eligible, 15 ineligible) - manual QA - [ ] T065 [US3] Verify skip reasons in notification and audit log - [ ] T066 [US3] Run tests: `./vendor/bin/sail artisan test tests/Feature/BulkPruneVersionsTest.php` **Checkpoint**: Policy versions pruning working, eligibility enforced, skip reasons logged --- ## Phase 8: User Story 4 - Bulk Delete Restore Runs (Priority: P2) **Goal**: Enable admins to delete completed/failed restore runs to declutter history **Independent Test**: Select 20 completed runs → bulk delete → verify soft-deleted, running runs skipped ### Tests for User Story 4 - [ ] T067 [P] [US4] Write unit test for deletable() scope in tests/Unit/RestoreRunDeletableTest.php - [ ] T068 [P] [US4] Write unit test for BulkRestoreRunDeleteJob in tests/Unit/BulkRestoreRunDeleteJobTest.php - [ ] T069 [P] [US4] Write feature test for bulk delete restore runs in tests/Feature/BulkDeleteRestoreRunsTest.php - [ ] T070 [P] [US4] Write test for mixed statuses (skip running) in tests/Feature/BulkDeleteMixedStatusTest.php ### Implementation for User Story 4 - [ ] T071 [P] [US4] Create BulkRestoreRunDeleteJob in app/Jobs/BulkRestoreRunDeleteJob.php - [ ] T072 [US4] Add bulk delete action to RestoreRunResource in app/Filament/Resources/RestoreRunResource.php - [ ] T073 [US4] Filter by deletable() scope (completed, failed, aborted only) - [ ] T074 [US4] Skip running restore runs with warning - [ ] T075 [US4] Add type-to-confirm for ≥20 runs - [ ] T076 [US4] Test delete with 20 completed + 5 running (manual QA, verify 5 skipped) - [ ] T077 [US4] Run tests: `./vendor/bin/sail artisan test tests/Feature/BulkDeleteRestoreRunsTest.php` **Checkpoint**: Restore runs bulk delete working, running runs protected, skip warnings shown --- ## Phase 9: Additional Resource - Bulk Delete Backup Sets (Priority: P2) **Goal**: Enable admins to delete backup sets with cascade-delete of backup items **Independent Test**: Select 10 backup sets → bulk delete → verify sets deleted, items cascade-deleted ### Tests for Additional Resource - [ ] T078 [P] Write unit test for BulkBackupSetDeleteJob in tests/Unit/BulkBackupSetDeleteJobTest.php - [ ] T079 [P] Write feature test for bulk delete backup sets in tests/Feature/BulkDeleteBackupSetsTest.php ### Implementation for Additional Resource - [ ] T080 [P] Create BulkBackupSetDeleteJob in app/Jobs/BulkBackupSetDeleteJob.php - [ ] T081 Add bulk delete action to BackupSetResource in app/Filament/Resources/BackupSetResource.php - [ ] T082 Verify cascade-delete logic for backup items (should be automatic via foreign key) - [ ] T083 Add type-to-confirm for ≥10 sets - [ ] T084 Test delete with 15 backup sets (manual QA) - [ ] T085 Run tests: `./vendor/bin/sail artisan test tests/Feature/BulkDeleteBackupSetsTest.php` **Checkpoint**: Backup sets bulk delete working, cascade-delete verified --- ## Phase 10: Polish & Cross-Cutting Concerns **Purpose**: Documentation, cleanup, performance optimization - [ ] T086 [P] Update README.md with bulk operations feature description - [ ] T087 [P] Update quickstart.md with manual testing scenarios (already done in planning) - [ ] T088 Code cleanup: Remove debug statements, refactor duplicated logic - [ ] T089 Performance test: Bulk delete 500 policies (should complete <5 minutes) - [ ] T090 Load test: Concurrent bulk operations (2-3 admins, different resources) - [ ] T091 [P] Security review: Verify tenant isolation in all jobs - [ ] T092 [P] Permission audit: Verify all bulk actions respect RBAC - [ ] T093 Run full test suite: `./vendor/bin/sail artisan test` - [ ] T094 Run Pint formatting: `./vendor/bin/sail composer pint` - [ ] T095 Manual QA checklist: Complete all scenarios from quickstart.md - [ ] T096 Document configuration options (chunk size, polling interval, retention days) - [ ] T097 Create BulkOperationRun resource page in Filament (view progress, retry failed) - [ ] T098 Add bulk operation metrics to dashboard (total runs, success rate) **Checkpoint**: Feature polished, tested, documented, ready for staging deployment --- ## Dependencies & Execution Order ### Phase Dependencies ``` Setup (Phase 1) ↓ Foundational (Phase 2) ← BLOCKS all user stories ↓ ┌───────────────────────────────────┐ │ User Stories (Parallel Capable) │ ├───────────────────────────────────┤ │ US1: Bulk Delete Policies (P1) │ ← MVP │ US2: Bulk Export (P1) │ ← MVP │ US5: Type-to-Confirm (P1) │ ← Embedded in US1 ├───────────────────────────────────┤ │ US6: Progress Tracking (P2) │ ← Enhances US1, US2 │ US3: Prune Versions (P2) │ │ US4: Delete Runs (P2) │ │ Additional: Delete Sets (P2) │ └───────────────────────────────────┘ ↓ Polish (Phase 10) ``` ### User Story Dependencies - **US1 (P1)**: Depends on Foundational (T007, T010, T011). Fully independent after that. - **US2 (P1)**: Depends on Foundational (T010). Fully independent after that. - **US5 (P1)**: Implemented within US1 (T020), tested separately. - **US6 (P2)**: Depends on US1 and US2 (adds progress to existing jobs). Can be implemented after US1/US2 are functional. - **US3 (P2)**: Depends on Foundational (T008, T010). Fully independent. - **US4 (P2)**: Depends on Foundational (T009, T010). Fully independent. - **Additional (P2)**: Depends on Foundational (T010). Fully independent. ### Parallel Opportunities Within Phases **Setup (Phase 1)**: - T003, T004 can run in parallel (different files) **Foundational (Phase 2)**: - T008, T009, T012 can run in parallel (different files) - T007, T010, T011 must be sequential (modify same services) **User Story 1 Tests (T014-T017)**: All parallel (different test files) **User Story 1 Implementation**: - T018 parallel with T019-T021 (different files initially) - T019-T021 sequential (same file edits) - T022-T025 sequential (testing/validation) **User Story 2 Tests (T026-T028)**: All parallel **User Story 2 Implementation**: - T029 parallel with T030-T031 (different files) - T030-T035 sequential (same file, progressive features) **User Story 3-4 and Additional**: Can all run in parallel after Foundational complete (different resources, no overlap) ### Critical Path (Fastest Route to MVP) ``` T001 → T002 → T005 (Setup migrations, run) ↓ T007 → T010 → T011 (Foundational: Policy scope, AuditLogger, SyncJob) ↓ T014-T017 (US1 tests) → T018 → T019 → T020 (US1 core) → T024 (verify) ↓ MVP Ready: Bulk delete policies with type-to-confirm ``` **Estimated MVP Time**: ~12-16 hours (Setup + Foundational + US1) --- ## Parallel Example: User Story 1 Implementation If working with a team, these tasks can run concurrently: **Developer A**: ```bash # Write tests first (TDD) tests/Unit/BulkPolicyDeleteJobTest.php (T014) tests/Feature/BulkDeletePoliciesTest.php (T015) ``` **Developer B** (after foundational complete): ```bash # Implement job app/Jobs/BulkPolicyDeleteJob.php (T018) ``` **Developer C** (after T018 complete): ```bash # Wire up Filament UI app/Filament/Resources/PolicyResource.php (T019-T021) ``` **Developer A** (after implementation complete): ```bash # Run tests, verify passing ./vendor/bin/sail artisan test tests/Feature/BulkDeletePoliciesTest.php (T024) ``` --- ## Parallel Example: Multiple User Stories After Foundational phase completes, these can proceed in parallel (different team members): **Team Member 1**: US1 Bulk Delete Policies (T014-T025) **Team Member 2**: US2 Bulk Export (T026-T035) **Team Member 3**: US3 Prune Versions (T055-T066) **Team Member 4**: US6 Progress Tracking (T043-T054) All integrate at the end without conflicts (different resources/files). --- ## Testing Strategy ### Unit Tests (tests/Unit/) - BulkPolicyDeleteJob - BulkPolicyExportJob - BulkPolicyVersionPruneJob - BulkBackupSetDeleteJob - BulkRestoreRunDeleteJob - BulkActionPermission - PolicyVersion::pruneEligible scope - RestoreRun::deletable scope - BulkOperationRun progress helpers - Circuit breaker logic ### Feature Tests (tests/Feature/) - BulkDeletePolicies (sync, async) - BulkExportToBackup - BulkTypeToConfirm - BulkProgressNotification - BulkPruneVersions (eligibility, skip reasons) - BulkDeleteRestoreRuns (mixed statuses) - BulkDeleteBackupSets (cascade) ### Manual QA Scenarios (from quickstart.md) 1. Bulk delete 10 policies (sync, no type-to-confirm) 2. Bulk delete 25 policies (async, type-to-confirm) 3. Bulk export 30 policies (progress tracking) 4. Bulk prune 30 versions (eligibility checks) 5. Circuit breaker with mock failures (>50% fail) --- ## Implementation Strategy ### MVP First (P1 Features Only) **Goal**: Ship minimal viable feature to get user feedback **Scope**: - Setup (T001-T006) - Foundational (T007-T013) - US1: Bulk Delete Policies (T014-T025) - US2: Bulk Export (T026-T035) - US5: Type-to-Confirm (T036-T042) - already in US1 **Estimated Time**: 16-22 hours **Deliverable**: Admins can bulk delete and export policies with safety gates ### Iteration 2 (P2 Features) **Goal**: Add progress tracking and additional resources **Scope**: - US6: Progress Tracking (T043-T054) - US3: Prune Versions (T055-T066) - US4: Delete Runs (T067-T077) - Additional: Delete Sets (T078-T085) **Estimated Time**: 10-12 hours **Deliverable**: Full feature set with progress UI and all resources ### Iteration 3 (Polish) **Goal**: Production readiness **Scope**: - Polish (T086-T098) - Performance testing - Security audit - Documentation finalization **Estimated Time**: 4-6 hours **Deliverable**: Production-ready feature --- ## Success Criteria - ✅ All P1 tests passing (US1, US2, US5) - ✅ Bulk delete 100 policies in <2 minutes - ✅ Type-to-confirm prevents accidents (≥20 items) - ✅ Progress updates every 5-10s for queued jobs - ✅ Audit log captures per-item outcomes - ✅ Circuit breaker aborts at >50% failure rate - ✅ Tenant isolation enforced (verified via tests) - ✅ Manual QA checklist complete (5 scenarios) --- ## Total Task Count - **Setup**: 6 tasks (T001-T006) - **Foundational**: 7 tasks (T007-T013) - **US1**: 12 tasks (T014-T025) - **US2**: 10 tasks (T026-T035) - **US5**: 7 tasks (T036-T042) - **US6**: 12 tasks (T043-T054) - **US3**: 12 tasks (T055-T066) - **US4**: 11 tasks (T067-T077) - **Additional**: 8 tasks (T078-T085) - **Polish**: 13 tasks (T086-T098) **Total**: 98 tasks **MVP Scope**: 35 tasks (Setup + Foundational + US1 + US2 + US5) **Full P1/P2 Scope**: 85 tasks (all user stories) **Production Ready**: 98 tasks (including polish) --- **Status**: Tasks Ready for Implementation **Next Step**: Begin Phase 1 (Setup) → Run migrations → Start US1 tests