feat(security): Settings → Maintenance with selectable SQLite backups manager#277
Merged
Conversation
Strip narrative comments from maintenance.ts and SecurityPane's MaintenanceRow / formatBytes. Keep WHY notes (BACKUP_FILENAME_RE scope, VACUUM no-transaction, stat/rm best-effort skips).
Reworks the Settings → Maintenance feature so users explicitly pick
which backup snapshots to delete instead of an opaque keep-N flow.
Removes the Vacuum DB row from this PR — it will land separately so
the (potentially main-thread-blocking) VACUUM concern is reviewed on
its own.
## Core
- Replace `cleanBackups({keep})` with `listBackups()` + `deleteBackups(names[])`.
- Broaden the on-disk match to `^spool-pre-.+\.db$` so manual rollback
snapshots (e.g. `spool-pre-pr5-revert-…`) are visible to the user;
the previous `v\d+` regex hid them. Each row is tagged `auto` or
`manual` so the UI can distinguish at a glance.
- `deleteBackups` rejects path-traversal names + non-matching filenames
before touching the filesystem.
- Tests cover list ordering, kind tagging, traversal rejection, empty
inputs, missing-file tolerance.
## UI
- New `BackupsManager` component with a multi-select list (max-h-72
scrollable) inside a single bordered box.
- List header: tri-state master checkbox (none / partial / all),
shows total when nothing selected, swaps to selected count + size
+ inline Delete button when selection exists. Fixed-height row
(`h-8`) so the appearance of the Delete button never causes layout
jitter.
- Two quick-action chips below the list: `Select auto (N)`,
`Select all but newest`.
- Delete is click-twice in-place (no modal); the button switches from
amber outline (idle) to filled amber (confirm) to spinner (busy).
## Feedback
- Delete success / failure / partial-success go through sonner toasts,
matching the share / import / undo pattern elsewhere in the app.
- Initial-load failure surfaces inline (toasts auto-dismiss, but the
list literally has nothing to render so the message has to stick).
## i18n
- 25 new `backups_*` keys; zh-CN and zh-TW are fully translated.
- ja / ko / de / fr keep the EN copy as stubs pending native review.
- The `_pendingNativeReview_maintenance` sentinel key is removed —
it served only the partial-translation state and is now noise.
## Test plan
- [x] core 315/315 vitest
- [x] app 229/229 vitest (incl. locale parity, plural-form audit)
- [x] typecheck clean
- [x] Manually verified in dev with mixed auto+manual seed:
- tri-state header behaves (none → all → clear)
- Select auto / Select all but newest correctness
- Delete success toast + actual file removal
- Empty-state copy correct after deleting every backup
- Initial-load failure path renders inline (not the misleading
"no backups" message)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a Maintenance section to Settings → Security letting users review and selectively delete the SQLite backup snapshots Spool writes before destructive migrations or manual rollbacks. Closes the long-standing "my ~/.spool/backups/ is several GB and I can't tell what's safe to delete" gap, which is a hard prerequisite for the Security Scan GA gate (per the backlog).
Scope is intentionally limited to backups only — Vacuum DB is deferred to its own PR so the (potentially main-thread-blocking) VACUUM concern gets reviewed on its own.
API surface
listBackups(db) → BackupFileInfo[]deleteBackups(db, names[]) → { deleted, bytesFreed }cleanBackups({ keep })backupDirFor(db)BackupFileInfocarriesname,sizeBytes,mtimeMs, andkind: 'auto' \| 'manual'. The regex listing allspool-pre-*.dbis wider than before (the oldv\d+form hid manual rollback snapshots from the UI);deleteBackupsindependently re-validates the name and rejects path traversal before touching the filesystem.UI behaviour
h-8) so the Delete button's appearance never jitters the layout.Select auto (N),Select all but newest.Feedback
i18n
backups_*keys. en is the source of truth.Test plan