Implements Spec 091 “BackupSchedule Retention & Lifecycle (Archive/Restore/Force Delete)”.
- BackupSchedule lifecycle:
- Archive (soft delete) with confirmation; restores via Restore action; Force delete with confirmation and strict gating.
- Force delete blocked when historical runs exist.
- Archived schedules never dispatch/execute (dispatcher + job guard).
- Audit events emitted for archive/restore/force delete.
- RBAC UX semantics preserved (non-member hidden/404; member w/o capability disabled + server-side 403).
- Filament UX contract update:
- Create CTA placement rule across create-enabled list pages:
- Empty list: only large centered empty-state Create CTA.
- Non-empty list: only header Create action.
- Tests added/updated to enforce the rule.
Verification:
- `vendor/bin/sail bin pint --dirty`
- Focused tests: BackupScheduling + RBAC enforcement + EmptyState CTAs + Create CTA placement
Notes:
- Filament v5 / Livewire v4 compliant.
- Manual quickstart verification in `specs/091-backupschedule-retention-lifecycle/quickstart.md` remains to be checked (T031).
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #109
178 lines
10 KiB
Markdown
178 lines
10 KiB
Markdown
---
|
||
|
||
description: "Executable task list for Spec 091 implementation"
|
||
---
|
||
|
||
# Tasks: BackupSchedule Retention & Lifecycle (Archive/Restore/Force Delete)
|
||
|
||
**Input**: Design documents from `/specs/091-backupschedule-retention-lifecycle/`
|
||
**Prerequisites**: `plan.md` (required), `spec.md` (user stories), `research.md`, `data-model.md`, `contracts/`, `quickstart.md`
|
||
|
||
**Tests**: REQUIRED (Pest) for runtime behavior changes.
|
||
|
||
---
|
||
|
||
## Phase 1: Setup (Shared Infrastructure)
|
||
|
||
**Purpose**: Confirm baseline assumptions and align with repo conventions before changing runtime behavior.
|
||
|
||
- [X] T001 Confirm SoftDeletes + lifecycle conventions in app/Filament/Resources/BackupSetResource.php
|
||
- [X] T002 Confirm RBAC-UX helper usage patterns in app/Support/Rbac/UiEnforcement.php
|
||
- [X] T003 Confirm BackupSchedule scheduling flow touchpoints in app/Services/BackupScheduling/BackupScheduleDispatcher.php and app/Jobs/RunBackupScheduleJob.php
|
||
|
||
---
|
||
|
||
## Phase 2: Foundational (Blocking Prerequisites)
|
||
|
||
**Purpose**: Introduce the archived lifecycle state at the data/model layer.
|
||
|
||
**⚠️ CRITICAL**: No user story work should begin until this phase is complete.
|
||
|
||
- [X] T004 Add SoftDeletes to app/Models/BackupSchedule.php
|
||
- [X] T005 Create migration to add deleted_at to backup_schedules in database/migrations/ (new migration file)
|
||
- [X] T006 [P] Update BackupSchedule model query expectations/tests to account for soft-deleted records in tests/Feature/BackupScheduling/BackupScheduleCrudTest.php
|
||
|
||
**Checkpoint**: `BackupSchedule` supports archived state (`deleted_at`).
|
||
|
||
---
|
||
|
||
## Phase 3: User Story 1 — Archive a schedule safely (Priority: P1) 🎯 MVP
|
||
|
||
**Goal**: Tenant admins can archive schedules (soft delete) with confirmation; archived schedules are excluded from the default list and MUST never execute.
|
||
|
||
**Independent Test**: Create an active schedule, archive it, verify it disappears from the default list, is visible under an “Archived” filter, audit entry exists, and scheduler/job do not dispatch/execute archived schedules.
|
||
|
||
### Tests (write first)
|
||
|
||
- [X] T007 [P] [US1] Add archive lifecycle tests in tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php
|
||
- [X] T008 [P] [US1] Add scheduler safety test (archived schedules not dispatched) in tests/Feature/BackupScheduling/DispatchIdempotencyTest.php
|
||
- [X] T009 [P] [US1] Add job safety test (archived schedule skipped) in tests/Feature/BackupScheduling/RunBackupScheduleJobTest.php
|
||
- [X] T009 [P] [US1] Add job safety test (archived schedule skipped) in tests/Feature/BackupScheduling/RunBackupScheduleJobTest.php (assert `OperationRun` is `completed` with outcome `blocked`, `summary_counts.skipped=1`, and `failure_summary` includes `code=schedule_archived`)
|
||
|
||
### Implementation
|
||
|
||
- [X] T010 [US1] Add “Archived” filter to BackupSchedule table in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T011 [US1] Remove/disable bulk destructive actions for schedules in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T012 [US1] Replace hard-delete semantics with “Archive” action (soft delete) in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T013 [US1] Enforce RBAC-UX (non-member→404; member missing capability→403) for archive action using app/Support/Rbac/UiEnforcement.php in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T014 [US1] Emit audit event backup_schedule.archived via app/Services/Intune/AuditLogger.php from app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T015 [US1] Ensure dispatcher excludes archived schedules in app/Services/BackupScheduling/BackupScheduleDispatcher.php
|
||
- [X] T016 [US1] Add execution-time guard to skip archived schedules in app/Jobs/RunBackupScheduleJob.php
|
||
- [X] T016 [US1] Add execution-time guard to skip archived schedules in app/Jobs/RunBackupScheduleJob.php (set `OperationRun` to `completed` + outcome `blocked`, `summary_counts.skipped=1`, `failure_summary.code=schedule_archived`, and emit `backup_schedule.run_skipped` with `reason=schedule_archived`)
|
||
|
||
**Checkpoint**: Archive works end-to-end; archived schedules never execute.
|
||
|
||
---
|
||
|
||
## Phase 4: User Story 2 — Restore an archived schedule (Priority: P2)
|
||
|
||
**Goal**: Tenant admins can restore archived schedules (no confirmation required) without changing enabled/disabled state.
|
||
|
||
**Independent Test**: Archive a schedule, restore it, verify it returns to the default list, `is_enabled` is unchanged, and audit entry exists.
|
||
|
||
### Tests (write first)
|
||
|
||
- [X] T017 [P] [US2] Add restore lifecycle tests (no confirmation required; is_enabled unchanged) in tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php
|
||
|
||
### Implementation
|
||
|
||
- [X] T018 [US2] Add “Restore” action (only for archived records) in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T019 [US2] Enforce RBAC-UX for restore action using app/Support/Rbac/UiEnforcement.php in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T020 [US2] Emit audit event backup_schedule.restored via app/Services/Intune/AuditLogger.php from app/Filament/Resources/BackupScheduleResource.php
|
||
|
||
**Checkpoint**: Restore is functional and independently testable.
|
||
|
||
---
|
||
|
||
## Phase 5: User Story 3 — Force delete as a privileged retention tool (Priority: P3)
|
||
|
||
**Goal**: Highly privileged tenant admins can force delete archived schedules with confirmation, but only when no historical runs exist.
|
||
|
||
**Independent Test**: Archive a schedule, attempt force delete without `TENANT_DELETE` (403), attempt force delete with historical runs (blocked), force delete with no historical runs (success), and audit entry exists.
|
||
|
||
### Tests (write first)
|
||
|
||
- [X] T021 [P] [US3] Add force delete tests (capability gated; only archived; blocked when historical runs exist) in tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php
|
||
|
||
### Implementation
|
||
|
||
- [X] T022 [US3] Add “Force delete” action (only for archived records; requires confirmation) in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T023 [US3] Enforce RBAC-UX for force delete using app/Support/Rbac/UiEnforcement.php in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T024 [US3] Block force delete when historical runs exist using BackupSchedule::operationRuns() in app/Models/BackupSchedule.php and enforce in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T024 [US3] Block force delete when historical runs exist using BackupSchedule::operationRuns() in app/Models/BackupSchedule.php and enforce in app/Filament/Resources/BackupScheduleResource.php (danger notification: title “Cannot force delete backup schedule”, body “Backup schedules referenced by historical runs cannot be removed.”; do not delete; do not emit `backup_schedule.force_deleted`)
|
||
- [X] T025 [US3] Emit audit event backup_schedule.force_deleted via app/Services/Intune/AuditLogger.php from app/Filament/Resources/BackupScheduleResource.php
|
||
|
||
**Checkpoint**: Force delete is safe (blocked when history exists) and audited.
|
||
|
||
---
|
||
|
||
## Phase 6: Polish & Cross-Cutting Concerns
|
||
|
||
**Purpose**: Ensure contract compliance, update impacted tests, and validate via quickstart.
|
||
|
||
- [X] T026 [P] Update bulk delete test to match “no bulk destructive actions” rule in tests/Feature/BackupScheduling/BackupScheduleBulkDeleteTest.php
|
||
- [X] T027 Ensure BackupSchedule list inspect affordance opens Edit page in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T028 Ensure “max 2 visible row actions; everything else in More ActionGroup” in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T029 Run formatting in repo using vendor/bin/sail bin pint --dirty
|
||
- [X] T030 Run focused test suite using vendor/bin/sail artisan test --compact tests/Feature/BackupScheduling
|
||
- [ ] T031 Run Spec 091 manual checklist from specs/091-backupschedule-retention-lifecycle/quickstart.md
|
||
|
||
---
|
||
|
||
## 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–5)**: Depend on Foundational completion.
|
||
- **Polish (Phase 6)**: Depends on all desired user stories being complete.
|
||
|
||
### User Story Dependencies
|
||
|
||
- **US1 (Archive)**: First; enables “Archived” state and execution safety.
|
||
- **US2 (Restore)**: Depends on US1 (requires archived records to exist).
|
||
- **US3 (Force delete)**: Depends on US1 (force delete only applies to archived records).
|
||
|
||
---
|
||
|
||
## Parallel Execution Examples
|
||
|
||
### US1 (after Phase 2)
|
||
|
||
- T007 (lifecycle tests), T008 (dispatcher safety test), and T009 (job safety test) can be written in parallel.
|
||
- T015 (dispatcher exclusion) and T016 (job guard) can be implemented in parallel.
|
||
|
||
### US3 (after US1)
|
||
|
||
- T021 (force delete tests) can be written while UI action scaffolding is prepared (T022–T023).
|
||
|
||
---
|
||
|
||
## Implementation Strategy
|
||
|
||
### MVP First (US1 Only)
|
||
|
||
1. Complete Phase 1 and Phase 2.
|
||
2. Implement US1 (archive + execution safety + audit) and pass US1 tests.
|
||
3. Validate via Phase 6 tasks T029–T031.
|
||
|
||
### Incremental Delivery
|
||
|
||
- Add US2 (restore) once US1 is stable.
|
||
- Add US3 (force delete) last, with strict capability gating and history blocking.
|
||
|
||
---
|
||
|
||
## Additions (Consistency + Constitution Coverage)
|
||
|
||
**Purpose**: Close remaining gaps found by the consistency analysis (terminology, FR-012 coverage, and explicit server-side RBAC enforcement + tests).
|
||
|
||
- [X] T032 [US1] Allow editing archived schedules (SoftDeletes) in app/Filament/Resources/BackupScheduleResource/Pages/EditBackupSchedule.php
|
||
- [X] T033 [P] [US1] Add test asserting an authorized tenant member can open Edit for a soft-deleted schedule in tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php
|
||
- [X] T034 [US1] Enforce server-side authorization (Policy/Gate) for Archive action in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T035 [US2] Enforce server-side authorization (Policy/Gate) for Restore action in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T036 [US3] Enforce server-side authorization (Policy/Gate) for Force delete action in app/Filament/Resources/BackupScheduleResource.php
|
||
- [X] T037 [P] [US1] Add explicit RBAC-UX tests for 404 vs 403 semantics in tests/Feature/BackupScheduling/BackupScheduleLifecycleAuthorizationTest.php
|
||
- [X] T038 [P] [US1] Add idempotency tests for Archive/Restore (already archived / already active) in tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php
|