> AI agents: this is one page from Mammoth Analytics documentation. The index of all pages as Markdown is https://docs.mammoth.io/llms.txt. Append `.md` to any docs URL, or send `Accept: text/markdown`, to get Markdown.

# Mammoth CLI blast radius and safety

> Six mutation classes and four confirmation policies. What each Mammoth command does to your data, and what it demands before it runs.

This is the part of the CLI that makes it reasonable to hand to an autonomous agent. Nothing has to be inferred from a command's name: the class and the policy are declared, and `mammoth schema get <command.id>` returns them at runtime.

## The six mutation classes

The class states what a command does to your data or account. Counts below are the CLI's commands on the release the [capability contract](https://docs.mammoth.io/build/) was generated from, as `mammoth schema list` reports them.

| Class | What it means | Commands |
| --- | --- | --- |
| `read` | Reads only. Cannot change anything. | 309 |
| `benign_mutation` | Changes something, but it is ordinary and reversible—create, update, rename. | 225 |
| `reversible_pipeline` | Adds or edits a pipeline step. Undo by removing or editing the step. | 40 |
| `external_effect` | Acts outside Mammoth—sends a message, writes to an external store or database. | 42 |
| `high_impact` | Changes account-level state: membership, ownership, workspace settings. | 99 |
| `destructive` | Deletes something. Recoverable only if the resource has a trash/restore path. | 66 |

> **`external_effect` is not undoable**
>
> It acts outside Mammoth, so it does not destroy anything inside your workspace—which is why it is not marked destructive—and it cannot be taken back, which is why it is not benign either.

Of the 781, 165—every `high_impact` and `destructive` command—can lose data or change your account. Filter the full list by class on [the capability contract](https://docs.mammoth.io/build/).

## The four confirmation policies

The policy states what you must pass before a command runs.

| Policy | How to satisfy it |
| --- | --- |
| `none` | Nothing required. |
| `prompt_or_yes` | Pass `--yes`, or confirm at an interactive terminal. Any delete, remove, clear or discard that cannot be undone uses this. |
| `yes_always` | Pass `--yes` always, even at a terminal. |
| `confirm_target` | Pass `--yes` and `--confirm TARGET`, matching exactly. The CLI checks `--confirm` before any API call. |

## Behaviour without a terminal

Prompts happen only when standard input is a real terminal. Under `--no-input`, in `json`/`ndjson` output, or in CI, there is no prompt. A missing `--yes` or `--confirm` fails with exit code `2` and an error code of `confirmation_required` or `confirmation_target_mismatch`.

```bash
# normal delete
mammoth dataset delete 2340 --project 180 --yes

# high-impact: --confirm must match the workspace id exactly
mammoth workspace delete --yes --confirm 9
```

Discover a command's policy at runtime with `mammoth schema get <command.id>`.

## The other safety rules

- The CLI never retries a mutation without a real server idempotency contract. **Only exit code 7 is safe to retry.**
- An interruption returns exit code `130` and closes sessions and files.
- Secrets never appear on the command line, in logs, or in any envelope.
- `--dry-run` on any API-backed command resolves parents, display names and conditions, then stops before the SDK call and reports what it would have sent (`data.would_call`, `data.dry_run: true`) along with `targets`, the named resources it would change. A dry run of a delete also lists its `dependents`, and a dry run of a row-changing step can carry `predicted_impact`. A destructive command whose targets cannot be resolved fails with `dry_run_targets_unresolvable` rather than reporting an empty list. No confirmation flag is needed for a dry run; the real run still needs its own.
- Every `view transform *` and `view task add` accepts `expected_task_count`, the task count your last read returned. The CLI re-reads the pipeline right before writing and refuses with `pipeline_changed` if the count no longer matches, so a plan made against a stale read cannot land on a pipeline that changed under it.

## Where the instruction came from

Every class and policy above describes what a command does. None of them describes who asked for it, and an agent reading a web page, an issue tracker or a spreadsheet is reading text somebody else wrote.

- **Data an agent reads is not an instruction.** A dataset cell, a file name or a column header can contain a sentence that looks like a command. Mammoth returns it as data, and it stays data.
- **Blast radius is the control that survives this.** An injected instruction still has to produce a call, and a `destructive` call still demands its confirmation. That is why the confirmation policy belongs to the command, not to the conversation.
- Be careful combining Mammoth with other tools in one agent session. Content one tool returns can be aimed at another, and the more tools share a context, the more surface that has.
- Credentials go through `mammoth auth login`, run by a human, never through a prompt—so no instruction an agent reads can cause it to repeat one back. See [Authenticate](https://docs.mammoth.io/agents/#authenticate).

## The inverse case: data entering Mammoth through a link, not an argument

Everything above is about an agent *acting on* Mammoth. The [Mammoth MCP server](https://docs.mammoth.io/agents/mcp) introduces the opposite direction: a tool argument only carries text, so a spreadsheet, a large CSV or anything binary cannot travel through the model at all. Its `request_upload` tool instead mints a one-time upload ticket and hands back a link; a person opens it in their own browser and drops a file on it.

What makes this safe to describe in the same vocabulary as everything above:

- **The link carries no Mammoth sign-in of its own.** The ticket stands in for the caller: it holds the MCP caller's own access token, sealed with the same key that guards Mammoth's other stored credentials, so a page has something to upload as without ever showing a sign-in form.
- **It is single-use and short-lived.** The ticket is 256 bits of randomness—the same strength as an OAuth authorization code—good for 30 minutes, and is spent (and the caller's token dropped from the record) the moment a file is uploaded, so the link opens nothing a second time.
- **The bytes never pass through the model.** They go from the browser straight to the same upload route the Mammoth web app itself uses, so size, format and every ordinary check and audit trail are the signed-in user's own, not the conversation's.
- **The upload runs as the person who asked for it, with their permissions and no others.** Nothing about the ticket grants the browser broader access than the MCP caller already had.
- **An expired or already-used link says so, in the browser, and offers no retry path of its own.** The agent has to ask its `request_upload` tool for a new one; there is no way to extend or replay an old link.

The class this belongs to, if you extend the six above by analogy rather than by a value the server actually declares: the file landing in Mammoth is an ordinary upload—`benign_mutation` in the CLI's own vocabulary—the ticket that authorizes it is the new part.

---

**Next up:** [Agents and CI](https://docs.mammoth.io/agents/ci) for non-interactive auth and the reliable operation loop · [Capability contract](https://docs.mammoth.io/build/) to filter every command by blast radius.

---
Source: https://docs.mammoth.io/agents/safety.md · Updated: 2026-10-21