# Safety and deletion

> What census reads, what it can delete, how it asks, and the checks every item passes before it is deleted.

## Reading is safe

Scanning and every query are read-only. `scan`, `top`, `ls`, `findings`, `large`, `duplicates`, `apps` and the other queries never move, change or delete your files. `export` writes only the report file you name. You can run any of them as often as you like.

## Deleting is permanent

> [!IMPORTANT]
> Nothing census deletes goes to the Trash. Once an item is deleted, it is gone.

Only these commands delete:

| Command | Deletes |
| --- | --- |
| [`clean`](https://diskcensus.com/docs/cli/clean.md#clean) | The safe tier of a fresh scan, or the safe items you name |
| [`review`](https://diskcensus.com/docs/cli/clean.md#review) | The items you choose in its terminal menu |
| [`apply`](https://diskcensus.com/docs/cli/clean.md#apply) | The paths in a selection file, if census listed them |
| [`uninstall`](https://diskcensus.com/docs/cli/clean.md#uninstall) | An app and the data named after it |
| [`optimize`](https://diskcensus.com/docs/cli/maintain.md#optimize) | The safe tier, then the quick maintenance tasks run |
| [`empty-trash`](https://diskcensus.com/docs/cli/clean.md#empty-trash) | What is in the Trashes you own |

`maintain` doesn't delete your files, but its tasks change your Mac: rebuilding caches, restarting Finder, deleting local Time Machine snapshots. It asks before it runs any of them.

## Safe, review and keep

Every finding has a safety tier, from the rule that recognized it.

| Tier | What it means | What happens to it |
| --- | --- | --- |
| `safe` | Rebuilds itself: caches, build output, logs, and AI agents' old versions. | `clean` deletes it when it isn't in use. |
| `review` | May cost time or bandwidth to get back, or may hold something worth keeping: installers, Cargo `target` folders, some app caches. | Waits in a review set until a person picks it. |
| `keep` | Shown with its size, never offered: AI agents' history, the Go module cache, conda package caches, simulator devices, Docker's disk image. | Never deleted. |

```sh
# Only what clean would delete
census findings --safety safe

# What waits for you
census findings --safety review
```

## How each command asks

In a terminal, every command that deletes shows what it will delete and asks first. Without a terminal (a script, an agent, or `census serve`) there is no one to ask, so you pass a flag that says you already agreed. Without that flag the command fails with `confirmation_required` (exit code 1) and deletes nothing.

| Command | In a terminal | Without asking |
| --- | --- | --- |
| `clean` | Lists the items and asks `[y/N]` | `--safe` |
| `review` | `Delete which? (1,3-5 · Enter for none)`, then `Type delete` | Use `review --json` and `apply` |
| `apply` | The selection file is your choice, so it doesn't ask | No flag needed |
| `uninstall` | Lists what goes and asks `[y/N]` | `--yes` |
| `optimize` | Shows the items and tasks, and asks once | `--yes` |
| `empty-trash` | `Permanently delete 1,218 items (8.5 GB)? [y/N]` | `--yes` |
| `maintain TASK` | Asks `[y/N]` | `--yes` |

`empty-trash --json` without `--yes` only lists what is in the Trash. It never deletes.

## What every item is checked against

All deletion goes through one executor. It works from a fresh scan, never from a list it was handed: `clean`, `apply`, `uninstall` and `optimize` rescan first, and an item is deleted only if the fresh scan still finds it under the same rule. Then, for each item:

- **Outside the scan's scope:** refused.
- **Protected folders:** refused. The root folders (`/`, `/Users`, `/Applications`, `/Library`, `/System`, `/private`), your home folder, `~/Library` and its standard folders, `~/Documents`, `~/Desktop`, `~/Downloads`, `~/Pictures`, `~/Movies` and `~/Music` are never deleted, and neither is a folder that contains one. The files inside them can be.
- **Synced folders:** refused. iCloud Drive (except its Trash, when you empty it), `~/Library/CloudStorage`, Dropbox and OneDrive. Cloud placeholders are refused too, except in a Trash you empty, because deleting one can delete the file from the cloud.
- **The System volume and SIP-protected items:** refused.
- **Links:** never followed. A symbolic link anywhere on the path skips the item.
- **Changed since the scan:** skipped. A file must have the same identity, size and modification time; a folder, the same identity.
- **In use:** skipped. See below.
- **Nested findings:** a `review` or `keep` finding inside a `safe` one stays, with the folders that lead to it.
- **Never forced:** census never runs `chmod` or `chflags`. A locked file or a permission error is reported, not overridden.

Two more rules apply to every run: census refuses to delete as root (`run_as_root`), and only one cleanup runs at a time (`cleanup_running`).

## Items in use

An item is skipped as `in_use`, and stays for review, when:

- a program you're running has a file open inside it;
- a program is running from inside it, such as a dev server from `node_modules` or an agent's current version;
- the app or tool it belongs to is running, such as Xcode for `DerivedData` or `cargo` for the Cargo caches;
- a launcher points to it, such as `~/.local/bin/claude` to the Claude Code version it starts.

Quit the app or tool, then run the command again.

## The audit log

Every run that deletes writes an audit log to `~/Library/Application Support/census/audit/`, one NDJSON file per run. Before each item it writes a `started` line, and after it the result, with the path, bytes and reason. The JSON result names the file in `results.audit`:

```json
{
  "audit": {
    "display": "/Users/me/Library/Application Support/census/audit/clean-1790950000000.ndjson",
    "lossy": false
  }
}
```

If the audit can't be written, census stops before the next item (`audit_unavailable`).

## Item outcomes

Every item in a cleanup result has an `outcome.result`:

| Result | Meaning | Reasons |
| --- | --- | --- |
| `deleted` | Gone. `bytes` is the space it took. | — |
| `partial` | Some of it was deleted, some couldn't be. | — |
| `skipped` | Left alone this time. | `missing`, `changed_since_scan`, `not_listed`, `in_use`, `cancelled`, `audit_unavailable`, `no_other_copy` |
| `failed` | Nothing could be deleted. Carries `errno` and `error`. | — |
| `refused` | Never attempted. | `invalid_path`, `outside_scope`, `protected`, `own_data`, `synced`, `system_volume`, `sip`, `mount_point`, `not_deletable`, `root` |

Skipped items are normal and still exit 0. Any `failed` or `partial` item makes the exit code 4 (`partial_cleanup`).
