bug: Backup retention purges all backups #6
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The backup retention system is incorrectly purging ALL backups older than 8 hours. The system should retain backups based on their classification categories (hourly, daily, weekly, monthly), but appears to only evaluate against the shortest retention period (8 hourly backups).
Current Behavior
All backups older than 8 hours are being deleted, regardless of whether they should be retained as daily, weekly, or monthly backups.
Expected Behavior
Backups should be retained based on a tiered classification system:
Key Retention Rules
Multi-category classification: A single backup can belong to multiple categories simultaneously
Expiration based on longest retention: The backup's expiration date should be calculated from the category with the longest retention period it qualifies for
Independent category quotas: Each category maintains its own count independent of others
Prerequisites (Investigation Required)
Before implementing the fix, verify:
Acceptance Criteria
Core Fix
UI Enhancement
Logging/Observability
Testing
Technical Notes
Classification Logic Pseudocode
Expiration Calculation
Definition of Done
Backup retention purges all backups based on hourly schedule instead of category-based retentionto bug: Backup retention purges all backupsPlan: Tiered Backup Retention Classification
Phase: Planning | Agent: Planner | Status: AWAITING_REVIEW
Overview
This plan implements tiered backup retention classification to replace the current per-schedule, count-based retention system. The core problem is that backups are being deleted prematurely because each schedule operates independently. The solution classifies each backup by timestamp into multiple categories (hourly, daily, weekly, monthly) and calculates expiration based on the longest retention period.
Approach: Add dedicated database columns (
categories TEXT[],expires_at TIMESTAMPTZ) for efficient queries. Classification occurs at backup creation time. Retention cleanup honors all categories before deleting.Planning Context
Decision Log
ANY()queries for category filtering -> JSONB would require JSON path queries with worse index support -> dedicated columns match existing repository patternRejected Alternatives
Constraints & Assumptions
Known Risks
Invisible Knowledge
Architecture
Classification Logic
Why This Structure
Milestones
Milestone 1: Database Migration & Types
Files:
backend/src/features/backup/migrations/002_add_retention_categories.sqlbackend/src/features/backup/domain/backup.types.tsbackend/src/features/backup/data/backup.repository.tsRequirements:
categories TEXT[]andexpires_at TIMESTAMPTZcolumns tobackup_historystarted_attimestampBackupHistoryTypeScript interface withcategoriesandexpiresAtmapHistoryRow()to convert new columnsAcceptance Criteria:
Tests:
Milestone 2: Classification Service
Files:
backend/src/features/backup/domain/backup-classification.service.ts(NEW)backend/src/features/backup/domain/__tests__/backup-classification.test.ts(NEW)Requirements:
classifyBackup(timestamp: Date): BackupCategory[]calculateExpiration(categories: BackupCategory[], timestamp: Date): DateTIERED_RETENTIONconstants:{ hourly: 8, daily: 7, weekly: 4, monthly: 12 }Acceptance Criteria:
['hourly', 'daily', 'weekly', 'monthly']['hourly']Tests:
backend/src/features/backup/domain/__tests__/backup-classification.test.tsMilestone 3: Retention Service Rewrite
Files:
backend/src/features/backup/domain/backup-retention.service.tsbackend/src/features/backup/data/backup.repository.tsRequirements:
processRetentionCleanup()to use tiered logicgetBackupsByCategory(category, limit)Acceptance Criteria:
Tests:
backend/src/features/backup/domain/__tests__/backup-retention.test.tsMilestone 4: Backup Creation Integration
Files:
backend/src/features/backup/jobs/backup-scheduled.job.tsbackend/src/features/backup/domain/backup.service.tsbackend/src/features/backup/data/backup.repository.tsRequirements:
ClassificationService.classifyBackup()during backup creationcategoriesandexpiresAtinbackup_historyrecordcreateBackupRecord()to accept categories and expiresAtAcceptance Criteria:
Tests:
Milestone 5: Frontend Desktop UI
Files:
frontend/src/pages/admin/AdminBackupPage.tsxfrontend/src/features/admin/types/admin.types.tsRequirements:
expiresAttoBackupHistorytypeAcceptance Criteria:
Tests:
Milestone 6: Frontend Mobile UI
Files:
frontend/src/features/admin/mobile/AdminBackupMobileScreen.tsxRequirements:
Acceptance Criteria:
Tests:
Milestone 7: Documentation
Files:
backend/src/features/backup/README.mdbackend/src/features/backup/CLAUDE.md(index update)Requirements:
Acceptance Criteria:
Milestone Dependencies
Verdict: AWAITING_REVIEW | Next: Plan review (QR plan-completeness)
Milestone 1: Database Migration & Types
Phase: Execution | Agent: Developer | Status: PASS
Completed
Created migration
002_add_retention_categories.sql:categories TEXT[]column with GIN indexexpires_at TIMESTAMPTZcolumn with indexstarted_attimestampexpires_atbased on longest retention periodUpdated
backup.types.ts:TIERED_RETENTIONconstants:{ hourly: 8, daily: 7, weekly: 4, monthly: 12 }BackupCategorytypecategoriesandexpiresAtfields toBackupHistoryinterfaceUpdated
backup.repository.ts:mapHistoryRow()to convert new columnscreateBackupRecord()to acceptcategoriesandexpiresAtVerification
Verdict: PASS | Next: M2 - Classification Service
Milestone 2: Classification Service + Tests
Phase: Execution | Agent: Developer | Status: PASS
Completed
Created
backup-classification.service.ts:classifyBackup(timestamp)- Returns array of categories based on UTC timecalculateExpiration(categories, timestamp)- Returns expiration date based on longest retentionclassifyAndCalculateExpiration(timestamp)- Convenience function for backup creationisFirstBackupOfDay(),isSunday(),isFirstDayOfMonth()Created
tests/unit/backup-classification.service.test.ts:Classification Logic
['hourly']['hourly', 'daily']['hourly', 'daily', 'weekly']['hourly', 'daily', 'monthly']['hourly', 'daily', 'weekly', 'monthly']Verification
Verdict: PASS | Next: M3 - Retention Service Rewrite
Milestone 3: Retention Service Rewrite
Phase: Execution | Agent: Developer | Status: PASS
Completed
Rewrote
backup-retention.service.tswith tiered logic:processRetentionCleanup()now uses unified tiered approachprocessTieredRetentionCleanup()implements category-based protectionbuildDeletionReason()provides human-readable deletion reasonsAdded repository methods in
backup.repository.ts:getBackupsByCategory(category)- Gets backups with specific categorygetAllCompletedBackups()- Gets all completed backups for tiered processingTiered Retention Logic
Logging
Each deletion logs:
Verification
Verdict: PASS | Next: M4 - Backup Creation Integration
Milestone 4: Backup Creation Integration
Phase: Execution | Agent: Developer | Status: PASS
Completed
backup.service.ts:classifyAndCalculateExpirationfrom classification servicecategoriesandexpiresAtin initial backup recordIntegration Flow
Example Log Output
Verification
Verdict: PASS | Next: M5 - Frontend Desktop UI
Milestone 5: Frontend Desktop UI
Phase: Execution | Agent: Developer | Status: PASS
Completed
Updated
admin.types.ts:BackupCategorytypecategoriesandexpiresAtfields toBackupHistoryinterfaceUpdated
AdminBackupPage.tsx:UI Change
Verification
Verdict: PASS | Next: M6 - Frontend Mobile UI
Milestone 6: Frontend Mobile UI
Phase: Execution | Agent: Developer | Status: PASS
Completed
AdminBackupMobileScreen.tsx:expiresAtis presentformatDate()helper for consistent formattingflex-wrapfor mobile layoutMobile Display
Verification
Verdict: PASS | Next: M7 - Documentation Updates
Milestone 7: Documentation Updates
Phase: Execution | Agent: Developer | Status: PASS
Completed
Updated
backend/src/features/backup/README.md:Created
backend/src/features/backup/CLAUDE.md:Documentation Added
Tiered Retention System section:
Verification
Verdict: PASS | All milestones complete
Implementation Complete
Phase: Execution | Status: ALL MILESTONES PASS
Summary
Implemented tiered backup retention classification system to replace per-schedule, count-based retention.
Core Fix:
Files Changed
Backend (New):
migrations/002_add_retention_categories.sqldomain/backup-classification.service.tstests/unit/backup-classification.service.test.tsCLAUDE.mdBackend (Modified):
domain/backup.types.tsdomain/backup.service.tsdomain/backup-retention.service.tsdata/backup.repository.tsREADME.mdFrontend (Modified):
types/admin.types.tspages/admin/AdminBackupPage.tsxmobile/AdminBackupMobileScreen.tsxAcceptance Criteria Status
Verification
Ready for code review and PR creation.