TenantAtlas/specs/091-backupschedule-retention-lifecycle/tasks.md
ahmido 1c098441aa feat(spec-091): BackupSchedule lifecycle + create-CTA placement rule (#109)
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
2026-02-14 13:46:06 +00:00

178 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 35)**: 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 (T022T023).
---
## 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 T029T031.
### 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