> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blobhub.io/llms.txt
> Use this file to discover all available pages before exploring further.

# blobhub workflow prune

> Delete the definitions the manifest does not cover — the one command that resolves drift

`prune` deletes every definition on the revision that no manifest entry claims. It is the only command in this
group that removes anything, and it is the last step of making a revision genuinely declarative: after a clean
`prune`, the revision holds exactly what the manifest says and nothing else.

**It is never implied by [`deploy`](/cli/workflow/commands/deploy).** Every command in this group that lists the
revision reports drift, leaves it untouched, and exits `0`. That is deliberate: a definition someone else added
in the browser is far more often work in progress than garbage, and a `deploy` that silently swept it away would
be a deployment tool deleting things nobody asked it to. Drift is reported everywhere and resolved only here,
only when you ask.

**Deletion is not recoverable through this CLI.** There is no undo, no trash, and no way to re-create a deleted
definition except by deploying its document again — which you only have if it was ever in your repository, and by
definition drift never was.

## Synopsis

```bash theme={null}
blobhub workflow prune -f <manifest> [--yes] [--dry-run]
```

| Flag           | Default  | Description                                                |
| -------------- | -------- | ---------------------------------------------------------- |
| `--file`, `-f` | required | Path to the workflow manifest.                             |
| `--yes`        | off      | Delete without prompting; required when not on a terminal. |
| `--dry-run`    | off      | Report what would be deleted and delete nothing.           |

There is no `--definition`. `prune` operates on the symmetric difference against the **entire** manifest — the
same set `deploy` already reports as drift — and narrowing that to a subset would make "everything the manifest
does not cover" mean something different on every run.

## What it does

1. Resolves the blob and revision. This is a **write**: status `ready`, phase `draft` or `managed`. A revision
   `deploy` would refuse, `prune` refuses too.
2. Lists both categories of definition and computes drift against the whole manifest.
3. Prints the full set, by `category/alias`, before asking anything.
4. Confirms — interactively, or via `--yes`.
5. Deletes each definition in turn and prints one line per deletion.

```text theme={null}
prune: 2 definition(s) on the revision are not in the manifest: playground/scratch_lab, workflow/legacy_flow
Delete these 2 definition(s)? [y/N] y
deleted playground/scratch_lab
deleted workflow/legacy_flow
```

With no drift, it does not prompt at all:

```text theme={null}
nothing to prune
```

Under `--json` the payload carries `deleted`, `would_delete`, `confirmed`, `dry_run` and `drift`, alongside the
envelope's `schema_version`. The `prune:` summary line is **not** part of that stream — like `execute --watch`'s
live lines it is suppressed under `--json`, so the envelope stays the only thing on stdout. In `--json` mode, pass
`--yes`: the payload is what a machine consumer reads, and an interactive prompt has no place in front of it.

## The set is printed before you are asked

The summary line comes first, always, and it names every definition by `category/alias` rather than reporting a
count. A bare alias would be ambiguous exactly where it matters most: an alias that names both a workflow and a
playground definition is two distinct deletions, and a count of two beside one name would read like a bug.

That line is not suppressed by `-q`, unlike the advisories. It is the destructive command's statement of intent,
not a warning someone can quiet down.

The prompt defaults to declining. Anything but `y` — including a bare Enter — is a no, and a declined run deletes
nothing and still **exits `0`**. Declining is a valid answer, not a failure.

## Confirmation, `--yes`, and no terminal

| Situation               | Behaviour                                  |
| ----------------------- | ------------------------------------------ |
| A terminal, no `--yes`  | Prompts. `[y/N]`, defaulting to no.        |
| `--yes`                 | Deletes without prompting.                 |
| No terminal, no `--yes` | `PRUNE_NOT_CONFIRMED`. Nothing is deleted. |

The third row is the one that matters for CI. A command that cannot prompt has two bad options — block forever on
a pipe that will never answer, or delete unattended because nobody objected. `prune` does neither: it refuses,
and tells you `--yes` is how to mean it.

## `--dry-run` beats `--yes`

`--dry-run` is checked **before** `--yes` and before the prompt. Passing both is not a contradiction to resolve
in favour of the more recent flag; the dry run wins:

```text theme={null}
prune: 2 definition(s) on the revision are not in the manifest: playground/scratch_lab, workflow/legacy_flow
would delete playground/scratch_lab
would delete workflow/legacy_flow
```

A stronger safety guard losing to a weaker one is the single outcome here that could surprise somebody, and the
cost of getting it wrong is unrecoverable. So `--dry-run --yes` deletes nothing, and a script that grew a
`--dry-run` on top of an existing `--yes` behaves the way whoever added it expected.

Unlike `--dry-run` on the other commands that carry it, this one is exact. The plan is the drift set, and the
drift set is what the run just computed — there is no compile step whose outcome a dry run cannot predict.

## If a deletion fails partway

Deletions are separate calls, made one at a time. If one fails, `prune` stops and re-raises the underlying API
code with the definitions it had **already deleted** named in the message:

```text theme={null}
error: API_TRANSIENT_ERROR: ... (already deleted: playground/scratch_lab)
```

Once an unrecoverable operation has failed partway, "which of mine are gone?" is the only question worth
answering, and naming the survivors would not answer it. Re-running `prune` is safe: the definitions that were
deleted are no longer on the revision, so they are no longer drift, and the second run's set is exactly what is
left.

## Errors

| Code                                                                                               | When                                                                                                                                                                                                                 |
| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MANIFEST_NOT_FOUND`, `MANIFEST_INVALID`, `MANIFEST_TYPE_MISMATCH`, `MANIFEST_VERSION_UNSUPPORTED` | The manifest is absent or unusable.                                                                                                                                                                                  |
| `DEFINITION_ALIAS_INVALID`, `DEFINITION_CATEGORY_UNKNOWN`                                          | An entry's alias fails the pattern; or its category cannot be inferred and none is declared, or the declared one contradicts the document. A manifest that will not load cannot define drift, so nothing is deleted. |
| `BLOB_NOT_ACCESSIBLE`, `BLOB_NOT_WORKFLOW`, `REVISION_NOT_ACCESSIBLE`, `REVISION_NOT_WRITABLE`     | The target cannot be resolved, or the revision is not `ready` in phase `draft`/`managed`.                                                                                                                            |
| `PRUNE_NOT_CONFIRMED`                                                                              | No terminal to confirm on and `--yes` was not passed. Nothing was deleted.                                                                                                                                           |
| `FORMAT_UNSUPPORTED`, `FORMAT_PARSE_ERROR`                                                         | A local `source` file has an unsupported extension, or does not parse — it is read to resolve the entry's category.                                                                                                  |
| `CREDENTIALS_NOT_FOUND`, `PROFILE_NOT_FOUND`, `INSECURE_CREDENTIALS_PERMISSIONS`                   | No usable credential, or a credentials file the CLI refuses to read.                                                                                                                                                 |
| `API_RATE_LIMITED`, `API_TRANSIENT_ERROR`, `API_NETWORK_ERROR`                                     | A call failed after its retries. If it was a delete, the message names what had already gone.                                                                                                                        |
| `DEFINITION_LIST_TRUNCATED`                                                                        | Advisory, on stderr. The exit code stays `0`.                                                                                                                                                                        |

`DEFINITION_LIST_TRUNCATED` deserves more caution here than anywhere else. Drift is computed from a listing the
platform cannot page, so a truncated listing means the set on screen may not be the whole set. It has never
caused `prune` to delete something it should not — a definition that never appeared cannot be deleted — but it
does mean a clean `prune` is not proof the revision now matches the manifest. See
[`ls`](/cli/workflow/commands/ls) for what the advisory means and when it can and cannot fire.

Every code above, with its remediation, is in [Error codes](/cli/error-codes).

## See also

* [`blobhub workflow ls`](/cli/workflow/commands/ls) — the same drift set, marked row by row, deleting nothing.
* [`blobhub workflow deploy`](/cli/workflow/commands/deploy) — reports this drift on every run and never acts on
  it.
* [`blobhub workflow diff`](/cli/workflow/commands/diff) — the read that still answers on a revision this command
  refuses to write to.
* [Delete Definition](/blob-types/workflow/operations/delete-definition) — the operation behind each deletion.
