13 KiB
Safety Rules for macOS Cleanup
Critical safety guidelines to prevent data loss and system damage.
Golden Rules
Rule 1: Never Delete Without Confirmation
ALWAYS ask user before deleting ANY file or directory.
Bad:
shutil.rmtree(cache_dir) # Immediately deletes
Good:
if confirm_delete(cache_dir, size, description):
shutil.rmtree(cache_dir)
else:
print("Skipped")
Rule 2: Explain Before Deleting
Users should understand:
- What is being deleted
- Why it's safe (or not safe)
- Impact of deletion
- Recoverability (can it be restored?)
Rule 3: When in Doubt, Don't Delete
If uncertain about safety: DON'T DELETE.
Ask user to verify instead.
Rule 4: Suggest Backups for Large Deletions
Before deleting >10 GB, recommend Time Machine backup.
Rule 5: Docker Prune Prohibition
NEVER use any Docker prune command. This includes:
docker image prune/docker image prune -adocker container prunedocker volume prune/docker volume prune -fdocker system prune/docker system prune -a --volumes
Why: Prune commands operate on categories, not specific objects. They can silently destroy database volumes, user uploads, and container state that the user intended to keep. A user who loses their MySQL data because of a prune command will never trust this tool again.
Correct approach: Always specify exact object IDs or names:
# Images: delete by specific ID
docker rmi a02c40cc28df 555434521374
# Containers: delete by specific name
docker rm container-name-1 container-name-2
# Volumes: delete by specific name
docker volume rm project-mysql-data project-redis-data
Rule 6: Double-Check Verification Protocol
Before deleting ANY Docker object, perform independent cross-verification. This applies to images, volumes, and containers.
Key requirements:
- For images: verify no container (running or stopped) references the image
- For volumes: verify no container mounts the volume
- For database volumes (name contains mysql, postgres, redis, mongo, mariadb): MANDATORY content inspection with a temporary container
- Even if Docker reports a volume as "dangling", the data inside may be valuable
See SKILL.md Step 4 for the complete verification commands and database volume inspection workflow.
Rule 7: Use Trash When Possible
Prefer moving to Trash over permanent deletion:
# Recoverable
osascript -e 'tell app "Finder" to move POSIX file "/path/to/file" to trash'
# Permanent (use only when confirmed safe)
rm -rf /path/to/file
Never Delete These
System Directories
| Path | Why | Impact if Deleted |
|---|---|---|
/System |
macOS core | System unbootable |
/Library/Apple |
Apple frameworks | Apps won't launch |
/private/etc |
System config | System unstable |
/private/var/db |
System databases | System unstable |
/usr |
Unix utilities | Commands won't work |
/bin, /sbin |
System binaries | System unusable |
User Data
| Path | Why | Impact if Deleted |
|---|---|---|
~/Documents |
User documents | Data loss |
~/Desktop |
User files | Data loss |
~/Pictures |
Photos | Data loss |
~/Movies |
Videos | Data loss |
~/Music |
Music library | Data loss |
~/Downloads |
May contain important files | Potential data loss |
Security & Credentials
| Path | Why | Impact if Deleted |
|---|---|---|
~/.ssh |
SSH keys | Cannot access servers |
~/Library/Keychains |
Passwords, certificates | Cannot access accounts/services |
| Any file with "credential", "password", "key" in name | Security data | Cannot authenticate |
Active Databases
| Pattern | Why | Impact if Deleted |
|---|---|---|
*.db, *.sqlite, *.sqlite3 |
Application databases | App data loss |
| Any database file for running app | Active data | Data corruption |
Running Applications
| Path | Why | Impact if Deleted |
|---|---|---|
/Applications |
Installed apps | Apps won't launch |
~/Applications |
User-installed apps | Apps won't launch |
Files in use (check with lsof) |
Currently open | App crash, data corruption |
Require Extra Confirmation
Large Deletions
Threshold: >10 GB
Action: Warn user and suggest Time Machine backup
Example:
⚠️ This operation will delete 45 GB of data.
💡 Recommendation:
Create a Time Machine backup first.
Check last backup:
tmutil latestbackup
Create backup now:
tmutil startbackup
Proceed without backup? [y/N]:
System-Wide Caches
Paths: /Library/Caches, /var/log
Action: Require manual sudo command (don't execute directly)
Example:
⚠️ This operation requires administrator privileges.
Please run this command manually:
sudo rm -rf /Library/Caches/*
⚠️ You will be asked for your password.
Reason:
- Requires elevated privileges
- User should be aware of system-wide impact
- Audit trail (user types password)
Docker Objects (Images, Containers, Volumes)
Action: List every object individually. Use precision deletion only (see Rule 5 and Rule 6).
NEVER use prune commands. Always specify exact IDs/names.
Example for volumes:
Docker volumes found:
postgres_data (1.2 GB) - Contains PostgreSQL database
redis_data (500 MB) - Contains Redis cache data
app_uploads (3 GB) - Contains user-uploaded files
Database volumes inspected with temporary container:
postgres_data: 8 databases, 45 tables, last modified 2 days ago
redis_data: 12 MB dump.rdb
Confirm EACH volume individually:
Delete postgres_data? [y/N]:
Delete redis_data? [y/N]:
Delete app_uploads? [y/N]:
Deletion commands (after confirmation):
docker volume rm postgres_data redis_data
Application Preferences
Path: ~/Library/Preferences/*.plist
Action: Warn that app will reset to defaults
Example:
⚠️ Deleting preferences will reset the app to defaults.
Impact:
- All settings will be lost
- Custom configurations will be reset
- May need to re-enter license keys
Only delete if:
- App is misbehaving (troubleshooting)
- App is confirmed uninstalled
Proceed? [y/N]:
Safety Checks Before Deletion
Check 1: Path Exists
if not os.path.exists(path):
print(f"❌ Path does not exist: {path}")
return False
Check 2: Not a System Path
system_paths = [
'/System', '/Library/Apple', '/private/etc',
'/usr', '/bin', '/sbin', '/private/var/db'
]
for sys_path in system_paths:
if path.startswith(sys_path):
print(f"❌ Cannot delete system path: {path}")
return False
Check 3: Not User Data
user_data_paths = [
'~/Documents', '~/Desktop', '~/Pictures',
'~/Movies', '~/Music', '~/.ssh'
]
expanded_path = os.path.expanduser(path)
for data_path in user_data_paths:
if expanded_path.startswith(os.path.expanduser(data_path)):
print(f"⚠️ This is a user data directory: {path}")
print(" Are you ABSOLUTELY sure? [type 'DELETE' to confirm]:")
response = input().strip()
if response != 'DELETE':
return False
Check 4: Not in Use
def is_in_use(path):
"""Check if file/directory is in use."""
try:
result = subprocess.run(
['lsof', path],
capture_output=True,
text=True
)
# If lsof finds processes using the file, returncode is 0
if result.returncode == 0:
return True
return False
except:
return False # Assume not in use if check fails
if is_in_use(path):
print(f"⚠️ Warning: {path} is currently in use")
print(" Close the application first, then try again.")
return False
Check 5: Permissions
def can_delete(path):
"""Check if we have permission to delete."""
try:
# Check parent directory write permission
parent = os.path.dirname(path)
return os.access(parent, os.W_OK)
except:
return False
if not can_delete(path):
print(f"❌ No permission to delete: {path}")
print(" You may need sudo, but be careful!")
return False
Safe Deletion Workflow
def safe_delete(path, size, description):
"""
Safe deletion workflow with all checks.
Args:
path: Path to delete
size: Size in bytes
description: Human-readable description
Returns:
(success, message)
"""
# Safety checks
if not os.path.exists(path):
return (False, "Path does not exist")
if is_system_path(path):
return (False, "Cannot delete system path")
if is_user_data(path):
if not extra_confirm(path):
return (False, "User cancelled")
if is_in_use(path):
return (False, "Path is in use")
if not can_delete(path):
return (False, "No permission")
# Backup warning for large deletions
if size > 10 * 1024 * 1024 * 1024: # 10 GB
if not confirm_large_deletion(size):
return (False, "User cancelled")
# Final confirmation
if not confirm_delete(path, size, description):
return (False, "User cancelled")
# Execute deletion
try:
if os.path.isfile(path):
os.unlink(path)
else:
shutil.rmtree(path)
return (True, f"Deleted successfully ({format_size(size)} freed)")
except Exception as e:
return (False, f"Deletion failed: {str(e)}")
Error Handling
Permission Denied
except PermissionError:
print(f"❌ Permission denied: {path}")
print(" Try running with sudo (use caution!)")
Operation Not Permitted (SIP)
# macOS System Integrity Protection blocks some deletions
except OSError as e:
if e.errno == 1: # Operation not permitted
print(f"❌ System Integrity Protection prevents deletion: {path}")
print(" This is a protected system file.")
print(" Do NOT attempt to bypass SIP unless you know what you're doing.")
Path Too Long
except OSError as e:
if e.errno == 63: # File name too long
print(f"⚠️ Path too long, trying alternative method...")
# Try using find + rm
Recovery Options
If User Accidentally Confirmed
Immediate action: Check Trash first
# Files may be in Trash
ls -lh ~/.Trash
Next: Time Machine
# Open Time Machine to date before deletion
tmutil browse
Last resort: File recovery tools
- Disk Drill (commercial)
- PhotoRec (free)
- TestDisk (free)
Note: Success rate depends on:
- How recently deleted
- How much disk activity since deletion
- Whether SSD (TRIM) or HDD
Preventing Accidents
- Use Trash instead of rm when possible
- Require Time Machine backup for >10 GB deletions
- Test on small items first before batch operations
- Show dry-run results before actual deletion
Red Flags to Watch For
User Requests
If user asks to:
- "Delete everything in ~/Library"
- "Clear all caches including system"
- "Delete all .log files on the entire system"
- "Remove all databases"
Response:
⚠️ This request is too broad and risky.
Let me help you with a safer approach:
1. Run analysis to identify specific targets
2. Review each category
3. Delete selectively with confirmation
This prevents accidental data loss.
Script Behavior
If script is about to:
- Delete >100 GB at once
- Delete entire directory trees without listing contents
- Run
rm -rf /or similar dangerous commands - Delete from system paths
Action: STOP and ask for confirmation
Testing Guidelines
Before Packaging
Test safety checks:
- ✅ Attempt to delete system path → Should reject
- ✅ Attempt to delete user data → Should require extra confirmation
- ✅ Attempt to delete in-use file → Should warn
- ✅ Attempt to delete without permission → Should fail gracefully
- ✅ Large deletion → Should suggest backup
In Production
Always:
- Start with smallest items
- Confirm results after each deletion
- Monitor disk space before/after
- Ask user to verify important apps still work
Summary
Conservative Approach
When implementing cleanup:
- Assume danger until proven safe
- Explain everything to user
- Confirm each step
- Suggest backups for large operations
- Use Trash when possible
- Test thoroughly before packaging
Remember
"It's better to leave 1 GB of unnecessary files than to delete 1 MB of important data."
User trust is fragile. One bad deletion loses it forever.
Final Checklist
Before any deletion:
- Path is verified to exist
- Path is not a system path
- Path is not user data (or extra confirmed)
- Path is not in use
- User has been informed of impact
- User has explicitly confirmed
- Backup suggested for large deletions
- Error handling in place
- Recovery options documented
Only then: proceed with deletion.