Harden destructive ops, persistence, and safety UX - #1
Merged
Conversation
Data safety / correctness: - FileSystemGuard.lockTarget is now crash-safe: move the original aside, create+lock the empty file, then delete the backup; restore on any failure so a partial failure can never destroy data. Added injectable seams (immutable-flag setter, lock-file creator) for the failure paths. - AppState preserves an undecodable/unreadable state file (move, then in-memory copy fallback) before any save can overwrite it, and blocks saving entirely if it can't be preserved. - Scheduled/unattended cleans downgrade the destructive "Lock with Immutable File" strategy to a plain wipe unless explicitly opted in (new Settings toggle, default off). - Centralised the in-flight clean mutex on AppState so the onboarding first-run clean can't race a scheduled/catch-up clean on the same paths. Safety UX: - Menu bar surfaces manual-blitz pass/fail outcome inline. - Reset All Settings now confirms before wiping state. - Dry-run flags delete-then-lock rows, offers the recovery script in place, and uses accurate reversibility copy. - Onboarding FDA step explains the grant, offers troubleshooting + a "Skip for now" escape; scan step distinguishes "needs access" from "not found" with an FDA banner. - Truthful replaceWithFile and Daemon Containers descriptions; accessible (labelled) level filter pills. Tests: +18 (now 40) covering lock partial-failure restore (both branches), corrupt/unreadable-state preservation + save-block, the clean mutex incl. real async-completion release, and the unattended-downgrade wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Single source of truth for level risk copy: ScanStepView now uses CleaningLevel.description instead of its own divergent strings. - TargetRowView shows a rotating disclosure chevron (and an accessibility hint) so the expand-on-tap detail is discoverable. - Onboarding clean banana suppresses its continuous spin under Reduce Motion and is marked decorative for VoiceOver. - Onboarding Continue responds to Return (.defaultAction), still gated by canProceed so it can't skip the FDA/scan steps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Hardens BananaBlitz's highest-blast-radius paths (it's a Full-Disk-Access, no-sandbox utility that wipes and immutably-locks
~/Librarydirectories) and closes the safety-UX gaps around them. All changes are build-verified withxcodebuild test(40 tests passing, +18 over baseline).Data safety / correctness
FileSystemGuard.lockTargetnow moves the original aside → creates+locks the empty file → deletes the backup, and restores on any failure. Previously it deleted first and could destroy data on a partial failure. A distinct error names the backup path if rollback itself can't restore.state.jsonis preserved (move, then in-memory-copy fallback) before any save can overwrite it, and saving is blocked if it can't be preserved — so one bad byte can no longer silently wipe history.AppState, so the onboarding first-run clean can't race a scheduled/catch-up clean on the same paths.Safety UX
replaceWithFile+Daemon Containerscopy; single-source level descriptions; labelled (accessible) filter pills; disclosure chevron on target rows; Reduce-Motion support.Tests
+18 covering: lock partial-failure restore (both
createFileand flag-set branches), corrupt/unreadable-state preservation + save-block, the clean mutex (incl. real async-completion release and the empty-workload path), and the unattended-downgrade wiring.Review process
The four data-safety fixes were put through an adversarial multi-lens review (failure-ordering, mutex-leak, regression, test-adequacy); the findings — most notably a backup-failure path that could have re-introduced the persistence data-loss bug — were resolved in this branch.
Deliberately deferred (need live-app verification or empirical validation)
CleanButtonrealButtonStyle/ focus ring (customDragGesturepress animations; needs eyes-on verification).Daemon Containers) was corrected.🤖 Generated with Claude Code