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
1.7 KiB
1.7 KiB
Data Model — Spec 091 (BackupSchedule Retention & Lifecycle)
Entities
BackupSchedule (existing)
Table: backup_schedules
Existing fields (selected)
id(pk)tenant_id(fk →tenants.id)name(string)is_enabled(bool)timezone(string)frequency(daily|weekly)time_of_day(time)days_of_week(json nullable)policy_types(json)include_foundations(bool)retention_keep_last(int)last_run_at(datetime nullable)last_run_status(string nullable)next_run_at(datetime nullable)created_at,updated_at
New fields (this spec)
deleted_at(datetime nullable)
State
- Active:
deleted_at = null - Archived:
deleted_at != null
Validation / rules (lifecycle)
- Archive is allowed only for active schedules.
- Restore is allowed only for archived schedules.
- Force delete is allowed only for archived schedules.
- Force delete is blocked if historical runs exist.
Relationships (existing)
tenant(): BelongsTooperationRuns(): HasMany(derived fromoperation_runsusingtenant_id+context->backup_schedule_id)
Operational Records
OperationRun (existing)
Historical runs for backup schedules are represented by operation_runs filtered via BackupSchedule::operationRuns().
Force delete constraint source
- “Historical runs exist” is defined as:
BackupSchedule::operationRuns()->exists().
Indexing / performance notes
- Add an index supporting common list queries for schedules:
- at minimum,
backup_schedules(deleted_at)for soft delete scoping - consider composite index like
backup_schedules(tenant_id, deleted_at)if query planner needs it
- at minimum,
No other schema changes are required for this feature.